? ? 我們知道 在RPG里單機游戲里 游戲存檔功能必不可少 在這里 網(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值 點擊讀檔為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()
{
// 判斷當前使用設(shè)備為蘋果手機 本例子針對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è)置默認不讀擋
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信息實際的屬性(切記:要理解兩個點 1.你把cube最后的位置給保存的屬性 最后的位置一定是可以決定cube位置的 比如說。cube.position系統(tǒng)方法 修改 cube.position cube的位置一定會被改變 2.讀檔時 你把保留下來的值給能決定當前對象的屬性 這樣一運行 就是你結(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的部分數(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腳本 就是按鈕的點擊存檔 以及讀檔 注意:游戲第一次運行 從來沒有存檔過 此時點擊讀檔會報錯? 解決方案已經(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;
// 程序一運行 就獲取isEnable的布爾值 如果此時還是假 也沒有關(guā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布爾值 為真才會去讀取 這就是能解決游戲一運行去點擊讀檔報錯的方案 一出來isEnable為false 不會執(zhí)行 方法 必須點擊過保存 才會變成true 然后用系統(tǒng)單例PlayerPrefs把isEnable為真保存起來 下次運行 就是 讀檔就可以被點擊了
if (isEnable)
{
g.gameData.isDu = true;//設(shè)置為可讀檔狀態(tài)
g.Save();//日志保存為可讀檔狀態(tài)
UnityEngine.SceneManagement.SceneManager.LoadScene(senceNum);//加載場景
senceNum++;
}
else
{
Debug.Log("還不能保存");
}
}