Unity下的簡單多賬號角色游戲存檔功能介紹

? ? 我們知道 在RPG里單機(jī)游戲里 游戲存檔功能必不可少 在這里 網(wǎng)上有個教程 非常的好 是基于xml下的保存 保存成dat文件(數(shù)據(jù)類型) 在這里 主要是針對網(wǎng)上那個教程做一個簡單的介紹 讓大家可以隨心所欲的用那個封裝好的存檔 可以自我增加功能


一、此套存檔腳本分為6個腳本? 先介紹哪些腳本 然后再具體腳本的作用(tag的取值是可以由用戶更改的 只是你要在相應(yīng)的腳本改成你自己設(shè)置的名字)

? ? ? ? 1.Sence腳本 2.GameDataManager腳本 3.Xmlsacer腳本 4.SenceData腳本 5.SaveCube腳本(這個腳本的名字是根據(jù)情況取的 在Demo中我是用來存檔cube位置信息) 6.UI腳本

二、這個腳本分別掛在哪些游戲?qū)ο笊?/p>

? ? 1.Sence腳本不需要掛在游戲?qū)ο笊?存在就行

? ? 2.GameDataManager腳本放在一個空物體上(取名GameDataManager)空物體Tag也設(shè)置取名GameDataManage

? ? 3.Xmlsacer腳本是xml保存數(shù)據(jù)的支撐腳本里面全是固定代碼不需要用戶編寫? 直接粘貼到工程就行 也放在名為GameDataManager空物體上

? 4.SenceData腳本 放在空物體上 空物體名字叫SenceData 空物體Tag也設(shè)置取名SenceData

? 5.SavePlayer 腳本也放在SenceData 空物體下玩家要設(shè)置tag值 隨意取名字 只是要在相應(yīng)腳本里把名字改成自己設(shè)的

6.U腳本 掛在空物體上 取名UI


三、各個腳本的作用 (在這里 我保存的是一個cube的位置信息 以及縮放比例)

? ? 1.sence腳本

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

[SerializeField]public class Sence

{

// 在這里聲明你要保存的屬性 其他的東西都不需要動

public Vector3 cubePoision ; // cube位置信息

public Vector3 cubeScale ; // cube的放大比例

}

public class sence : MonoBehaviour {

// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

}

2.GameDataManage腳本 注意保存存檔名稱和保存路徑即可 其他不需要動

using System.Collections;

using System.Collections.Generic;

using UnityEngine;?

using System.IO ; // 文件輸入與輸出 操作文件讀取和寫入時用到

using System.Collections.Generic ;

using System.Text ;

using System.Xml ;

using System.Security.Cryptography ;

using System ;

//GameData,儲存數(shù)據(jù)的類,把需要儲存的數(shù)據(jù)定義在GameData之內(nèi)就行//

public class GameData

{

// 密鑰 用于防止拷貝存檔

public string key ;

// 需要存儲的內(nèi)容 現(xiàn)在只存儲一個bool值 點(diǎn)擊讀檔為true 加載場景后為false

public bool isDu ;

// 場景存儲內(nèi)容

public Sence s ;

public GameData()

{

s = new Sence (); // 存儲內(nèi)容初始化 只能放構(gòu)造函數(shù)

isDu = false;

}

}

public class GameDataManager : MonoBehaviour {

//? 存檔文件的名稱 特別注意一下這條代碼 "csData1.dat" 如果你想實現(xiàn)多賬號存儲 這里 你要穿過來賬號的名稱 每次賬號不同 保存文件名不同 文件名不同 保存的存檔就不同 這就是為什么可以多賬號存儲的原因

private string dataFileNmae = "csData1.dat" ;

public GameData gameData ;

private? XmlSaver xs = new XmlSaver();

void Awake()

{

gameData = new GameData ();

// 設(shè)置密鑰 根據(jù)具體平臺設(shè)定

gameData.key = SystemInfo.deviceUniqueIdentifier ;

Load ();

}

// 讀檔時調(diào)用的方法

public void Load()

{

string gameDataFile = GetDataPath () + "/" + dataFileNmae;

if (xs.hasFile (gameDataFile)) {

string dataString = xs.LoadXML (gameDataFile);

GameData gameDataFromXML = xs.DeserializeObject (dataString, typeof(GameData)) as GameData;

// 如果是合法存檔

if (gameDataFromXML.key == gameData.key) {

gameData = gameDataFromXML;

} else {

// 游戲啟動后數(shù)據(jù)清零 存檔后作弊用

}

} else {

if (gameData != null) {

Save ();

}

}

}

public void Save()

{

string gameDataFile = GetDataPath() + "/"+dataFileNmae;

string dataString= xs.SerializeObject(gameData,typeof(GameData));

xs.CreateXML(gameDataFile,dataString);

}

// 獲取路徑

private static string GetDataPath()

{

// 判斷當(dāng)前使用設(shè)備為蘋果手機(jī) 本例子針對pc端 移動端返回路徑根據(jù)蘋果系統(tǒng)與安卓系統(tǒng)的不同自己設(shè)置

if (Application.platform == RuntimePlatform.IPhonePlayer) {

return null;

} else {

// 返回系統(tǒng)文件路徑 這里返回的是Application.dataPath 如果你想返回Application.dataPath下的文件夾 自己在后面加文件夾名字

return Application.dataPath? ;

}

}

// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

}?


3.Xmlsacer腳本位固定代碼 直接拷貝就行 不用管

using UnityEngine;

using System.Collections;

using System.Xml;

using System.Xml.Serialization;

using System.IO;

using System.Text;

using System.Security.Cryptography;

using System;

public class XmlSaver :MonoBehaviour{

//內(nèi)容加密

public string Encrypt(string toE)

{

//加密和解密采用相同的key,具體自己填,但是必須為32位//

byte[] keyArray = UTF8Encoding.UTF8.GetBytes("12348578902223367877723456789012");

RijndaelManaged rDel = new RijndaelManaged();

rDel.Key = keyArray;

rDel.Mode = CipherMode.ECB;

rDel.Padding = PaddingMode.PKCS7;

ICryptoTransform cTransform = rDel.CreateEncryptor();

byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toE);

byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray,0,toEncryptArray.Length);

return Convert.ToBase64String(resultArray,0,resultArray.Length);

}

//內(nèi)容解密

public string Decrypt(string toD)

{

//加密和解密采用相同的key,具體值自己填,但是必須為32位//

byte[] keyArray = UTF8Encoding.UTF8.GetBytes("12348578902223367877723456789012");

RijndaelManaged rDel = new RijndaelManaged();

rDel.Key = keyArray;

rDel.Mode = CipherMode.ECB;

rDel.Padding = PaddingMode.PKCS7;

ICryptoTransform cTransform = rDel.CreateDecryptor();

byte[] toEncryptArray = Convert.FromBase64String(toD);

byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray,0,toEncryptArray.Length);

return UTF8Encoding.UTF8.GetString(resultArray);

}

public string SerializeObject(object pObject,System.Type ty)

{

string XmlizedString? = null;

MemoryStream memoryStream? = new MemoryStream();

XmlSerializer xs? = new XmlSerializer(ty);

XmlTextWriter xmlTextWriter? = new XmlTextWriter(memoryStream, Encoding.UTF8);

xs.Serialize(xmlTextWriter, pObject);

memoryStream = (MemoryStream)xmlTextWriter.BaseStream;

XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());

return XmlizedString;

}

public object DeserializeObject(string pXmlizedString , System.Type ty)

{

XmlSerializer xs? = new XmlSerializer(ty);

MemoryStream memoryStream? = new MemoryStream(StringToUTF8ByteArray(pXmlizedString));

XmlTextWriter xmlTextWriter? = new XmlTextWriter(memoryStream, Encoding.UTF8);

return xs.Deserialize(memoryStream);

}

//創(chuàng)建XML文件

public void CreateXML(string fileName,string thisData)

{

string xxx = Encrypt(thisData);

StreamWriter writer;

writer = File.CreateText(fileName);

writer.Write(xxx);

writer.Close();

}

//讀取XML文件

public string LoadXML(string fileName)

{

StreamReader sReader = File.OpenText(fileName);

string dataString = sReader.ReadToEnd();

sReader.Close();

string xxx = Decrypt(dataString);

return xxx;

}

//判斷是否存在文件

public bool hasFile(String fileName)

{

return File.Exists(fileName);

}

public string UTF8ByteArrayToString(byte[] characters? )

{

UTF8Encoding encoding? = new UTF8Encoding();

string constructedString? = encoding.GetString(characters);

return (constructedString);

}

public byte[] StringToUTF8ByteArray(String pXmlString )

{

UTF8Encoding encoding? = new UTF8Encoding();

byte[] byteArray? = encoding.GetBytes(pXmlString);

return byteArray;

}

}


4.SenceData腳本. 這里你要把你所保存的東西的腳本名字tag值修改成你自己的

using System.Collections;

using System.Collections.Generic;

using UnityEngine;public class SenceData : MonoBehaviour

{// Use this for initializationvoid Start () {SaveCube sc = gameObject.GetComponent();

sc.Load ();

//末尾設(shè)置默認(rèn)不讀擋

GameObject gameDataManager = GameObject.Find ("GameDataManager");

GameDataManager g = gameDataManager.GetComponent();

g.gameData.isDu = false;

g.Save ();

}

public void Save (){

/存檔數(shù)據(jù)//Cube信息的保存SaveCube sc = gameObject.GetComponent();

sc.Save();

}

// Update is called once per frame

void Update () {

}

}


5.SaveCube腳本 這里就是存檔的本質(zhì)了 在游戲結(jié)束后 把最后cube的屬性值賦予給sence腳本里你要保存的信息 同時 在讀檔時 把保存下來的給控制cube信息實際的屬性(切記:要理解兩個點(diǎn) 1.你把cube最后的位置給保存的屬性 最后的位置一定是可以決定cube位置的 比如說。cube.position系統(tǒng)方法 修改 cube.position cube的位置一定會被改變 2.讀檔時 你把保留下來的值給能決定當(dāng)前對象的屬性 這樣一運(yùn)行 就是你結(jié)束游戲時那個樣子 )

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class SaveCube : MonoBehaviour {

// Use this for initialization

void Start () {

}

// Update is called once per frame

void Update () {

}

// 保存cube的部分?jǐn)?shù)據(jù) 這里只保存cube的位置信息

public void Save(){GameObject cube = GameObject.Find ("Cube");

GameObject gameDataManager = GameObject.Find ("GameDataManager");?

這里是保存cube在游戲結(jié)束時最后的位置 把它賦予給你在sence腳本創(chuàng)的屬性

// 拿到游戲?qū)ο蠊芾淼哪_本GameDataManager g = gameDataManager.GetComponent() ;

g.gameData.s.cubePoision = cube.transform.position;

g.gameData.s.cubeScale = cube.transform.localScale;

}

////// 場景開始時加載游戲?qū)ο?br>

///public void Load(){GameObject cube = GameObject.Find("Cube");

這里是賦值 cube在游戲結(jié)束時保存的位置 把它賦予給能決定cube位置的屬性

//獲得Cube對象GameObject gameDataManager =GameObject.Find("GameDataManager");

//獲得GameDataManager(游戲控制器)對象

GameDataManager g = gameDataManager.GetComponent();

//獲得GameDataManager(游戲控制器)腳本

if(g.gameData.isDu == true){//如果是讀檔狀態(tài)

cube.transform.position = g.gameData.s.cubePoision;//讀取Cube的位置,并賦值給Cube

cube.transform.localScale = g.gameData.s.cubeScale ;

}

}

}


6. UI腳本 就是按鈕的點(diǎn)擊存檔 以及讀檔 注意:游戲第一次運(yùn)行 從來沒有存檔過 此時點(diǎn)擊讀檔會報錯? 解決方案已經(jīng)在代碼體現(xiàn) 讀者自己理解下

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

private GameDataManager g;//定義GameDataManager(游戲控制器)腳本

private SenceData s;//定義SenceData(場景加載器)腳本

public int senceNum;

void Awake()

{

Instant = this;

// 程序一運(yùn)行 就獲取isEnable的布爾值 如果此時還是假 也沒有關(guān)系 當(dāng)游戲第二次運(yùn)行 就會變成真 那么 就讀檔 存檔正常了

if (PlayerPrefs.GetString(LoginManager.instance.userName)=="true")

{

isEnable = true;

}

}

void Start ()

{GameObject gameDataManager = GameObject.Find ("GameDataManager");

//獲得GameDataManager(游戲控制器)對象

g = gameDataManager.GetComponent();

//獲得GameDataManager(游戲控制器)腳本

GameObject senceData = GameObject.Find ("SenceData");

//獲得SenceData(場景加載器)對象

s = senceData.GetComponent();//獲得SenceData(場景加載器)腳本

}

// 你的存檔按鈕方法回調(diào)

public void SaveBtn()

{

s.Save ();//場景提交數(shù)據(jù)

g.Save ();//日志保存

isEnable = true;

PlayerPrefs.SetString(LoginManager.instance.userName,"true");

}

// 讀檔按鈕

public void ReadBtn()

{

// isEnable布爾值 為真才會去讀取 這就是能解決游戲一運(yùn)行去點(diǎn)擊讀檔報錯的方案 一出來isEnable為false 不會執(zhí)行 方法 必須點(diǎn)擊過保存 才會變成true 然后用系統(tǒng)單例PlayerPrefs把isEnable為真保存起來 下次運(yùn)行 就是 讀檔就可以被點(diǎn)擊了

if (isEnable)

{

g.gameData.isDu = true;//設(shè)置為可讀檔狀態(tài)

g.Save();//日志保存為可讀檔狀態(tài)

UnityEngine.SceneManagement.SceneManager.LoadScene(senceNum);//加載場景

senceNum++;

}

else

{

Debug.Log("還不能保存");

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容