我们知道 在RPG里单机游戏里 游戏存档功能必不可少 在这里 网上有个教程 非常的好 是基于xml下的保存 保存成dat文件(数据类型) 在这里 主要是针对网上那个教程做一个简单的介绍 让大家可以随心所欲的用那个封装好的存档 可以自我增加功能
一、此套存档脚本分为6个脚本 先介绍哪些脚本 然后再具体脚本的作用(tag的取值是可以由用户更改的 只是你要在相应的脚本改成你自己设置的名字)
1.Sence脚本 2.GameDataManager脚本 3.Xmlsacer脚本 4.SenceData脚本 5.SaveCube脚本(这个脚本的名字是根据情况取的 在Demo中我是用来存档cube位置信息) 6.UI脚本
二、这个脚本分别挂在哪些游戏对象上
1.Sence脚本不需要挂在游戏对象上 存在就行
2.GameDataManager脚本放在一个空物体上(取名GameDataManager)空物体Tag也设置取名GameDataManage
3.Xmlsacer脚本是xml保存数据的支撑脚本里面全是固定代码不需要用户编写 直接粘贴到工程就行 也放在名为GameDataManager空物体上
4.SenceData脚本 放在空物体上 空物体名字叫SenceData 空物体Tag也设置取名SenceData
5.SavePlayer 脚本也放在SenceData 空物体下玩家要设置tag值 随意取名字 只是要在相应脚本里把名字改成自己设的
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,储存数据的类,把需要储存的数据定义在GameData之内就行//
public class GameData
{
// 密钥 用于防止拷贝存档
public string key ;
// 需要存储的内容 现在只存储一个bool值 点击读档为true 加载场景后为false
public bool isDu ;
// 场景存储内容
public Sence s ;
public GameData()
{
s = new Sence (); // 存储内容初始化 只能放构造函数
isDu = false;
}
}
public class GameDataManager : MonoBehaviour {
// 存档文件的名称 特别注意一下这条代码 "csData1.dat" 如果你想实现多账号存储 这里 你要穿过来账号的名称 每次账号不同 保存文件名不同 文件名不同 保存的存档就不同 这就是为什么可以多账号存储的原因
private string dataFileNmae = "csData1.dat" ;
public GameData gameData ;
private XmlSaver xs = new XmlSaver();
void Awake()
{
gameData = new GameData ();
// 设置密钥 根据具体平台设定
gameData.key = SystemInfo.deviceUniqueIdentifier ;
Load ();
}
// 读档时调用的方法
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 {
// 游戏启动后数据清零 存档后作弊用
}
} 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()
{
// 判断当前使用设备为苹果手机 本例子针对pc端 移动端返回路径根据苹果系统与安卓系统的不同自己设置
if (Application.platform == RuntimePlatform.IPhonePlayer) {
return null;
} else {
// 返回系统文件路径 这里返回的是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{
//内容加密
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);
}
//内容解密
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);
}
//创建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 ();
//末尾设置默认不读挡
GameObject gameDataManager = GameObject.Find ("GameDataManager");
GameDataManager g = gameDataManager.GetComponent();
g.gameData.isDu = false;
g.Save ();
}
public void Save (){
/存档数据//Cube信息的保存SaveCube sc = gameObject.GetComponent();
sc.Save();
}
// Update is called once per frame
void Update () {
}
}
5.SaveCube脚本 这里就是存档的本质了 在游戏结束后 把最后cube的属性值赋予给sence脚本里你要保存的信息 同时 在读档时 把保存下来的给控制cube信息实际的属性(切记:要理解两个点 1.你把cube最后的位置给保存的属性 最后的位置一定是可以决定cube位置的 比如说。cube.position系统方法 修改 cube.position cube的位置一定会被改变 2.读档时 你把保留下来的值给能决定当前对象的属性 这样一运行 就是你结束游戏时那个样子 )
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的部分数据 这里只保存cube的位置信息
public void Save(){GameObject cube = GameObject.Find ("Cube");
GameObject gameDataManager = GameObject.Find ("GameDataManager");
这里是保存cube在游戏结束时最后的位置 把它赋予给你在sence脚本创的属性
// 拿到游戏对象管理的脚本GameDataManager g = gameDataManager.GetComponent() ;
g.gameData.s.cubePoision = cube.transform.position;
g.gameData.s.cubeScale = cube.transform.localScale;
}
////// 场景开始时加载游戏对象
///public void Load(){GameObject cube = GameObject.Find("Cube");
这里是赋值 cube在游戏结束时保存的位置 把它赋予给能决定cube位置的属性
//获得Cube对象GameObject gameDataManager =GameObject.Find("GameDataManager");
//获得GameDataManager(游戏控制器)对象
GameDataManager g = gameDataManager.GetComponent();
//获得GameDataManager(游戏控制器)脚本
if(g.gameData.isDu == true){//如果是读档状态
cube.transform.position = g.gameData.s.cubePoision;//读取Cube的位置,并赋值给Cube
cube.transform.localScale = g.gameData.s.cubeScale ;
}
}
}
6. UI脚本 就是按钮的点击存档 以及读档 注意:游戏第一次运行 从来没有存档过 此时点击读档会报错 解决方案已经在代码体现 读者自己理解下
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的布尔值 如果此时还是假 也没有关系 当游戏第二次运行 就会变成真 那么 就读档 存档正常了
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(场景加载器)脚本
}
// 你的存档按钮方法回调
public void SaveBtn()
{
s.Save ();//场景提交数据
g.Save ();//日志保存
isEnable = true;
PlayerPrefs.SetString(LoginManager.instance.userName,"true");
}
// 读档按钮
public void ReadBtn()
{
// isEnable布尔值 为真才会去读取 这就是能解决游戏一运行去点击读档报错的方案 一出来isEnable为false 不会执行 方法 必须点击过保存 才会变成true 然后用系统单例PlayerPrefs把isEnable为真保存起来 下次运行 就是 读档就可以被点击了
if (isEnable)
{
g.gameData.isDu = true;//设置为可读档状态
g.Save();//日志保存为可读档状态
UnityEngine.SceneManagement.SceneManager.LoadScene(senceNum);//加载场景
senceNum++;
}
else
{
Debug.Log("还不能保存");
}
}
网友评论