美文网首页
Unity下的简单多账号角色游戏存档功能介绍

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

作者: Unity与iOS的灵魂小白 | 来源:发表于2017-05-31 22:22 被阅读487次

        我们知道 在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下的简单多账号角色游戏存档功能介绍

          本文链接:https://www.haomeiwen.com/subject/udgjfxtx.html