Unity下的简单多账号角色游戏存档功能介绍

    我们知道 在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("还不能保存");

}

}

你可能感兴趣的:(Unity下的简单多账号角色游戏存档功能介绍)