Unity進(jìn)階技巧 - XML存檔與加密

數(shù)據(jù)與加密

前言

不管開發(fā)什么游戲苛吱,游戲存檔是個必不可少的功能,你可能需要保存玩家的一些信息阶剑,比如身上穿戴的裝備,玩家角色所處的場景等各種信息危号,對于存檔功能(數(shù)據(jù)持久化)牧愁,Unity提供了原生技術(shù)Playerprefs,它的優(yōu)點(diǎn)是理解和使用起來十分簡單外莲,缺點(diǎn)是對于大型數(shù)據(jù)存儲時會力不從心猪半,所以本文會介紹如何使用XML來實現(xiàn)游戲存檔和存檔加密的功能。

編程環(huán)境

  • Unity 5.2.2
  • OS X EI Capitan 10.11.6

你將學(xué)到什么偷线?

  • 如何使用XML對數(shù)據(jù)進(jìn)行序列化和反序列化操作磨确。
  • 如何對數(shù)據(jù)進(jìn)行加密和解密操作。
  • 不同平臺下文件存放的具體路徑和規(guī)則淋昭。

整體思路解析

數(shù)據(jù)存儲和加密的主要邏輯思路:

  1. 使用XmlSerializer類對需要保存的數(shù)據(jù)類進(jìn)行序列化操作俐填,得到一串字符串。
  2. 將得到的字符串使用RijndaeManaged類和ICrytoTransform類進(jìn)行加密操作翔忽,獲得加密之后的字符串英融。
  3. 根據(jù)平臺類型盏檐,確定文件保存的路徑。
  4. 使用StreamWriter類將字符串保存到文件中驶悟。

數(shù)據(jù)加載和解密的主要邏輯思路:

  1. 根據(jù)存檔文件的路徑胡野,使用StreamReader類讀取文件中的內(nèi)容(一串加密過的字符串)。
  2. 使用RijndaeManaged類和ICrytoTransform類對讀取的文件內(nèi)容進(jìn)行解密操作痕鳍,獲得一串解密后的字符串硫豆。
  3. 使用XmlSerializer類對解密后的字符串進(jìn)行反序列化操作,獲得具體的游戲數(shù)據(jù)笼呆,并使用數(shù)據(jù)對游戲中的數(shù)據(jù)進(jìn)行轉(zhuǎn)換操作熊响。

一、準(zhǔn)備工作

在實現(xiàn)具體保存操作前诗赌,我們需要先實現(xiàn)我們要保存的游戲數(shù)據(jù)汗茄,本文使用一個簡化的數(shù)據(jù),假設(shè)我們保存的數(shù)據(jù)是MyPlayer類铭若,里面記錄著以下信息:

  • 玩家的名字
  • 玩家的等級
  • 玩家的武器(包括物品ID洪碳,物品名字信息)
  • 玩家的衣服(包括物品ID,物品名字信息)

下面我們就來具體實現(xiàn)這些數(shù)據(jù)叼屠,首先我們新建一個Unity工程瞳腌,工程名字大家可以自定義,然后我們新建一個C#文件镜雨,命名為Item嫂侍,打開item文件,并進(jìn)行如下編輯:

Item代碼
  • 在Item類中荚坞,我們定義了兩個公共成員吵冒,_itemID和_name,分別代表物品的ID和名字西剥。
  • 然后我們在構(gòu)造函數(shù)里面初始化了物品ID和名稱。
  • 最后我們又創(chuàng)建了一個構(gòu)造函數(shù)亿汞,可以通過參數(shù)來指定Item的id和名字瞭空。

接下來我們再新建一個C#文件,命名為MyPlayer疗我,打開Myplayer文件咆畏,并進(jìn)行如下編輯:

MyPlayer代碼
  • 在MyPlayer類中,我們定義了4個公共成員吴裤,他們是_id旧找,_name,_weapon和_clothes麦牺,他們分別代表玩家的ID钮蛛,名字鞭缭,所擁有的武器和衣服。
  • 然后我們實現(xiàn)了MyPlayer的構(gòu)造函數(shù)魏颓,在構(gòu)造函數(shù)里面我們實例化了他的4個公共成員岭辣。

二、實現(xiàn)XmlManager的序列化與寫入操作

有了需要存儲的數(shù)據(jù)甸饱,下面我們可以來實現(xiàn)如何將數(shù)據(jù)用XML來序列化沦童,并且將序列化的數(shù)據(jù)寫入到文件中。

首先我們新建一個C#腳本叹话,命名為XmlManager偷遗,然后打開腳本進(jìn)行如下編輯:

XmlManager關(guān)于序列化和寫入的代碼
  • 通過上圖我們可以看到,在XmlManager腳本中驼壶,我們實現(xiàn)了3個方法
  • 在serializeObject方法中我們首先創(chuàng)建MemoryStream對象氏豌,這是為了后面我們創(chuàng)建XmlTextWriter類時的準(zhǔn)備,因為我們會通過指定流和編碼方式來創(chuàng)建XmlTextWriter的實例對象辅柴。
  • 接著我們通過指定數(shù)據(jù)類型創(chuàng)建了一個Xmlserializer的實例對象xs箩溃,然后通過調(diào)用xs的Serialize方法對傳入的pObject進(jìn)行序列化。
  • 然后我們把xmlTextWriter.BaseStream強(qiáng)制轉(zhuǎn)換成MemoryStream類型碌嘀,并賦值給mStream涣旨。
  • 最后通過調(diào)用UTF8ByteArrayToString方法將mStream數(shù)據(jù)轉(zhuǎn)換成string類型,并返回數(shù)據(jù)股冗。
  • 在CreateXMl方法中霹陡,我們通過傳入的參數(shù),指定了文件保存的位置止状,以及需要保存的具體數(shù)據(jù)烹棉,然后通過StreamWriter類將數(shù)據(jù)寫入到文件中。
  • 在UTF8ByteArrayToString方法中怯疤,我們通過UTF8Encoding將byte[]類型數(shù)據(jù)轉(zhuǎn)換成了String類型浆洗。

三、實現(xiàn)GameDataManager保存操作

首先在場景中新增一個空的對象集峦,然后將其命名為DataController伏社,然后在上面掛載一個我們新建的C#腳本GameDataManager

DataController對象與其掛載的腳本

然后我們打開GameDataManager腳本進(jìn)行如下的編輯:

GameDataManager的保存和獲取路徑代碼

編寫上圖中的代碼后塔淤,我們回到Unity編輯器摘昌,然后運(yùn)行,之后我們便會發(fā)現(xiàn)Project視窗中多了一個名為ZuiData的文件高蜂,如下圖:

**Project**視窗中**ZuiData**文件

然后我們打開ZuiData文件仰泻,就會發(fā)現(xiàn)里面保存著_myPlayer對象的數(shù)據(jù)论矾,如下圖:

ZuiData的文件內(nèi)容

到此,我們學(xué)會了將游戲數(shù)據(jù)序列化并寫入到文件中的操作了艾蓝。

四、實現(xiàn)XmlManager的反序列化和讀取操作

學(xué)會了保存數(shù)據(jù)后,下一步我們就要來實現(xiàn)讀取文件數(shù)據(jù),并且將其反序列化,成為我們可以使用的對象脂崔。

我們再次打開XmlManager腳本,新增以下代碼:

XmlManager中的反序列化和讀取代碼
  • 首先我們看deserializeObject方法梧喷,我們通過傳入的參數(shù)ty砌左,確定XmlSerializer需要反序列化的類型,然后需要反序列化的內(nèi)容從string轉(zhuǎn)換成byte[]類型铺敌,最后調(diào)用xs.Deserialize方法進(jìn)行反序列化操作汇歹,并返回其結(jié)果。
  • 接著我們看loadXML方法偿凭,有一個參數(shù)产弹,是需要讀取的文件名稱,然后創(chuàng)建一個StreamReader類型的對象弯囊,然后調(diào)用其方法ReadToEnd進(jìn)行讀取操作痰哨,最后返回讀取的內(nèi)容
  • stringToUTF8ByteArrayhasFile就很好理解了,一個是將string類型裝換成byte[]類型匾嘱,一個是通過文件名判斷該文件是否存在斤斧。

五、實現(xiàn)GameDataManager的讀取數(shù)據(jù)操作

實現(xiàn)了XmlManager的反序列化和讀取操作后霎烙,我們就可以在GameDataManager中實現(xiàn)將xml的數(shù)據(jù)讀取撬讽,并且把這些數(shù)據(jù)轉(zhuǎn)換成我們需要使用的類型,比如轉(zhuǎn)換成我們_myPlayer的信息悬垃。

接下來游昼,我們打開GameDataManager腳本,并新增以下代碼:

GameDataManager的讀取和打印代碼
  • 首先在load方法中尝蠕,先獲取文件存儲的路徑烘豌,然后判斷文件是否存在,如果不存在看彼,則在后臺打印提示信息扇谣,接下來,調(diào)用xm.loadXML方法讀取文件中的數(shù)據(jù)闲昭,讀取出來的數(shù)據(jù)是一段字符串,然后我們在調(diào)用xm.deserializeObject方法把數(shù)據(jù)轉(zhuǎn)換成MyPlayer類型的數(shù)據(jù)靡挥,最后如果數(shù)據(jù)不為空序矩,我們就把這些數(shù)據(jù)賦值給_myPlayer對象。
  • pressLoadButton方法是后面我們制作讀取按鈕時會用的方法跋破,里面主要做了兩件事情簸淀,一是調(diào)用load方法瓶蝴,讀取數(shù)據(jù),二是調(diào)用printData方法打印_myPlayer的部分屬性租幕。
  • printData方法中舷手,我們調(diào)用Debug.log方法打印出我們想要看的_myPlayer的屬性,而這里我們打印的屬性劲绪,是后面我們修改過具體內(nèi)容的幾個數(shù)據(jù)男窟,打印出來就是為了查看是否修改成功。

制作讀取按鈕

有了上面這些方法后贾富,為了在實際演示中歉眷,讓我們可以看到數(shù)據(jù)的讀取后的改變,我們在項目中新建一個按鈕颤枪,來觸發(fā)數(shù)據(jù)讀取的操作汗捡。

讀取數(shù)據(jù)按鈕

我們在項目中新建一個名為“LoadButton”的按鈕,然后將其內(nèi)容改為“讀取數(shù)據(jù)”畏纲,然后我們在按鈕的點(diǎn)擊邏輯上掛載GameDataManager中的pressLoadButton方法扇住,如下圖:

掛載pressLoadButton方法到LoadButton上

實現(xiàn)按鈕后,我們打開ZuiData文件盗胀,將玩家的ID改為99艘蹋,玩家名字改為“ZuiPlayer”,武器的名稱改為“Eagles”读整,如下圖:

修改ZuiData文件內(nèi)容

修改之后簿训,我們再次打開GameDataManager腳本,把Start方法中的內(nèi)容全部注釋掉米间,如下圖:

注釋Start方法中的代碼

最后我們回到Unity編輯器强品,然后運(yùn)行程序,點(diǎn)擊讀取數(shù)據(jù)按鈕屈糊,然后查看后臺打印出來的數(shù)據(jù)是否與我們修改過后的數(shù)據(jù)一致的榛,如無意外,效果如下圖:

最終效果圖

到此逻锐,我們學(xué)會了XML數(shù)據(jù)的讀取和反序列化夫晌。

六、對文件數(shù)據(jù)進(jìn)行加密和解密

雖然我們現(xiàn)在學(xué)會了使用xml進(jìn)行數(shù)據(jù)的存儲和讀取昧诱,但是就想我們上面讀取操作時一樣晓淀,我們可以直接通過改寫ZuiData文件里面的內(nèi)容,從而改變游戲的數(shù)據(jù)盏档,這樣對于游戲數(shù)據(jù)來說是很不安全的凶掰,所以我們最好對游戲最終保存的數(shù)據(jù)進(jìn)行一些加密操作,這樣就無法通過文件直接修改游戲的數(shù)據(jù)了。

我們再次打開XmlManager腳本懦窘,然后新增以下代碼:

XmlManager中加密和解密的代碼
  • 首先我們在前面引入了新的頭文件System.Security.Cryptography前翎,我們下面需要用的RijndaelManagedICryptTransform類,都是屬于其中畅涂。
  • 接下來我們定義了我們加密和解密所需要用的密鑰港华,具體的數(shù)字可以自定義,但是必須一共是32位午衰。
  • 然后我們先跳到上圖最后的getRijndaelManaged方法立宜,這里面我們主要是創(chuàng)建并定義我們加密和解密的方式,我們定義了一個RijndaelManaged對象苇经,然后設(shè)置密鑰為_keyArray(也就是我們之前定義的密鑰)赘理,然后設(shè)置對稱解密算法的運(yùn)算模式和填充模式(關(guān)于運(yùn)算模式和填充模式的詳細(xì)說明,大家可以參見最后面的參考鏈接)扇单,最后我們返回RijndaelManaged的實例化對象商模。
  • 接下來我們看到encrypt方法,這里面我們主要是把傳入的數(shù)據(jù)蜘澜,進(jìn)行加密操作施流,然后返回加密后的數(shù)據(jù),首先我們定義了一個ICryptTransform類型的對象鄙信,并且他是加密模式的瞪醋,然后我們把需要加密的數(shù)據(jù)類型轉(zhuǎn)換成byte[]類型,調(diào)用TransformFinalBlock方法得到加密后的數(shù)據(jù)装诡,最后將這個數(shù)據(jù)轉(zhuǎn)換成string類型并返回银受。
  • 最后我們來看decrypt方法,這里我們主要把傳入的數(shù)據(jù)鸦采,進(jìn)行解密操作宾巍,然后返回解密后的數(shù)據(jù),首先我們定義了一個ICryptTransform類型的對象渔伯,并且他是解密模式的顶霞,然后我們把需要解密的數(shù)據(jù)轉(zhuǎn)換成byte[]類型,調(diào)用TransformFinalBlock方法獲得解密后的數(shù)據(jù)锣吼,最后將這個數(shù)據(jù)轉(zhuǎn)換成string類型并返回选浑。

完成了這些主要的方法后,我們還需要對XmlManager腳本進(jìn)行一些小修改玄叠,具體見下圖:

XmlManager中的小修改

做完這些改動后古徒,我們再把GameDataManager腳本中的Start方法中的代碼注釋取消掉,讓其恢復(fù)作用读恃,最后我們回到Unity編輯器描函,運(yùn)行程序崎苗,可以看到我們save和load操作都是正常運(yùn)行的,而這個時候我們再次打開ZuiData文件舀寓,就會發(fā)現(xiàn)文件中的內(nèi)容變成了一些亂碼,這樣就無法通過修改存檔來改變游戲的數(shù)據(jù)了肌蜻。

加密后的ZuiData文件內(nèi)容

參考閱讀

MSDN 關(guān)于RijndaelManaged類的說明
MSDN 關(guān)于ICryptoTransform類的說明

補(bǔ)充內(nèi)容

  • 需要序列化的成員必須是public的互墓,私有的是不會被序列化的,如果某個成員變量不想序列化蒋搜,有兩種方法篡撵,一是設(shè)置為私有,二是使用[XmlIgnore]修飾豆挽,如下圖:
_clothes就不會被序列化
  • 如果一個成員變量的值為null育谬,序列化時不會記錄任何信息。
  • 自定義序列化類帮哈,必須要有默認(rèn)的構(gòu)造函數(shù)(即不帶任何參數(shù)的構(gòu)造函數(shù))膛檀,否則會報錯。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娘侍,一起剝皮案震驚了整個濱河市咖刃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憾筏,老刑警劉巖嚎杨,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異氧腰,居然都是意外死亡枫浙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門古拴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箩帚,“玉大人,你說我怎么就攤上這事斤富「喑保” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵满力,是天一觀的道長焕参。 經(jīng)常有香客問我,道長油额,這世上最難降的妖魔是什么叠纷? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮潦嘶,結(jié)果婚禮上涩嚣,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好航厚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布顷歌。 她就那樣靜靜地躺著,像睡著了一般幔睬。 火紅的嫁衣襯著肌膚如雪眯漩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天麻顶,我揣著相機(jī)與錄音赦抖,去河邊找鬼。 笑死辅肾,一個胖子當(dāng)著我的面吹牛队萤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播矫钓,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼要尔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了份汗?” 一聲冷哼從身側(cè)響起盈电,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杯活,沒想到半個月后匆帚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旁钧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年吸重,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歪今。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嚎幸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寄猩,到底是詐尸還是另有隱情嫉晶,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布田篇,位于F島的核電站替废,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏泊柬。R本人自食惡果不足惜椎镣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兽赁。 院中可真熱鬧状答,春花似錦冷守、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馆截,卻和暖如春授翻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孙咪。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巡语,地道東北人翎蹈。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像男公,于是被迫代替她去往敵國和親荤堪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容