游戏中常常需要保存数据就是存档,unity已经提供一种非常方便的数据存储的方式:

PlayerPrefs:数据持久化方案   采用键值对的方式对数据进行存储,可以存储Int,Float,String类型的数据,这是unity提供用来存储简单的数据,如果需要存储复杂和大量数据的话一般通过序列化(Serialize)来保存数据

序列化(Serialize),可以用来将对象转化为字节流,

反序列化(Deserialize),可以用来将字节流转化为对象

常见的数据序列化方法:二进制方法,XML方法,JSON方法

三者之间对比:

  • 二进制方法:简单,但可读性差
  • XML方法:可读性强,但是文件庞大,冗余信息多
  • JSON方法:数据格式比较简单,易于读写,但是不直观,可读性比XML差

实例: 
比如飞行射击游戏《飞机大战》中需要保存玩家数据包括生命值,得分,技能数以及当前关卡敌机位置和类型,现在这些数据都要保存在本地, 就需要用到序列化存储这些数据,下面分别介绍二进制,XML和JSON三种方法实现游戏的存档和读档
首先写好一个存储游戏数据类,定义好需要保存的数据

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.Serialization;
using System;
/// <summary>
///  存储游戏数据类
/// </summary>
[Serializable] //可序列化标志 
public class SaveGameData{

public List<SerializableVector3> livingEnemyPostions = new List<SerializableVector3>();
    public List<int> livingEnemyTypes = new List<int>();
    public int score = 0;
    public int hp = 0;
    public int skillCount = 0;

}
/*
 * 序列化在unity中的注意点
 * 不可以直接序列化Unity特有的数据类型(例如Vector3, Quaternion),必须要转换一下
*/
//在Vector3 和 SerializableVector3之间自动转换
[Serializable]
public struct SerializableVector3
{
    public float x;
    public float y;
    public float z;
    //构造函数
    public SerializableVector3(float rX, float rY, float rZ)
    {
        x = rX;
        y = rY;
        z = rZ;
    }

// 以字符串形式返回,方便调试查看
    public override string ToString()
    {
        return String.Format("[{0}, {1}, {2}]", x, y, z);
    }

// 隐式转换:将SerializableVector3 转换成 Vector3
    //implicit关键字属于转换运算符,表示隐式的类型转换,可以让我们自定义的类型支持相互交换。
    public static implicit operator Vector3(SerializableVector3 rValue)
    {
        return new Vector3(rValue.x, rValue.y, rValue.z);
    }
     
    // 隐式转换:将Vector3 转成 SerializableVector3
    public static implicit operator SerializableVector3(Vector3 rValue)
    {
        return new SerializableVector3(rValue.x, rValue.y, rValue.z);
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

using System.IO;        //实现文件流所必需的库
using System.Runtime.Serialization.Formatters.Binary;   //实现二进制串行化必需的库
using LitJson;
using System.Xml;

public enum GameState
{
    playing,
    gameOver,
    pause
}
public class GameManager : MonoBehaviour {
    public static GameManager Instance;
    public GameObject gameoverPanel;
    public GameObject saveMenu;
    public Transform spawn;
    
    //左上角显示分数
    public Text txt_Score;
    //游戏结束面板当前分数
    public Text txt_CurScore;
    //历史分数
    public Text txt_HistoryScore;
    //玩家当前生命值
    public Text txt_CurHP;
    public Text messageText;
    public Transform pause;
    private int score;
    private GameObject player;
    private string path;
    private  GameState gs;
    public GameState GS
    {
        get { return gs; }
    }
    private void Awake()
    {
        Instance = this;
        //清空对象池
        BulletPool.ClearPool();
        path = Application.dataPath + "/StreamingFile";  //路径为根目录自定义StreamingFile文件夹下
        //path = Application.streamingAssetsPath;    路径为根目录StreamingAssets文件夹下
    }
    void Start () {
        UpdateScore(0);
        player = GameObject.FindGameObjectWithTag("Player");
        gs = GameState.playing;
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {//按下ESC键调出Menu菜单,并将游戏状态改为暂停
            Pause();
        }
    }
    /// <summary>
    /// 切换游戏状态
    /// </summary>
    /// <param name="_gs"></param>
    public void SetGameState(GameState _gs)
    {
        gs = _gs;
        if (gs==GameState.gameOver)
        {
            Debug.Log("game over!");
        }
    }
    private void Pause()
    {
        gs = GameState.pause;
        Time.timeScale = 0;
        saveMenu.SetActive(true);
        pause.GetComponent<Image>().sprite = Resources.Load<Sprite>("ButtonIcon/game_resume");
    }
    private void UnPause()
    {
        gs = GameState.playing;
        Time.timeScale = 1;
        saveMenu.SetActive(false);
        pause.GetComponent<Image>().sprite = Resources.Load<Sprite>("ButtonIcon/game_pause");
    }
    public void ContinueGame()
    {
        UnPause();
        ShowMessage("");
    }
    /// <summary>
    /// 点击按钮控制是否暂停游戏
    /// </summary>
    public void GamePause()
    {
        if (gs == GameState.playing)
        {
            Pause();
        }
        else
        {
            UnPause();
        }
    }
    public void UpdateScore(int _score)
    {
        score += _score;
        //更新当前得分
        txt_Score.text = score.ToString();
    }
    public void UpdateHP(int hp)
    {
        txt_CurHP.text = hp.ToString();
    }

//显示提示信息
    public void ShowMessage(string str)
    {
        messageText.text = str;
    }
    void SaveDate(int historyScore)
    {
        //先取出历史数据
        if (score > historyScore)
        {//如果当前得分大于历史分数则覆盖历史最高分
            PlayerPrefs.SetInt("score", score);
        }
    }
    public void GameOver()
    {
        gs = GameState.gameOver;
        //显示当前分数
        txt_CurScore.text = score.ToString();
        //取出历史最高分
        int historyScore = PlayerPrefs.GetInt("score", 0);
        //显示最高分
        txt_HistoryScore.text = historyScore.ToString();
        //每次游戏结束需要把最高分保存下来
        SaveDate(historyScore);
        gameoverPanel.SetActive(true);    
    }
    /// <summary>
    /// 重新开始游戏
    /// </summary>
    public void Restart()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene("01_play");
        UnPause();
    }
    /// <summary>
    /// 退出游戏
    /// </summary>
    public void Quit()
    {
        Application.Quit();
    }
    /// <summary>
    /// 保存游戏
    /// </summary>
    public void SaveGame()
    {
        //SaveByBinary();
        //SaveByJson();
        SaveByXml();
    }
    /// <summary>
    /// 加载游戏
    /// </summary>
    public void LoadGame()
    {
        //LoadByBinary();
        //LoadByJson();
        LoadByXml();
       
    }
    /// <summary>
    /// 创建SaveGameData对象并存储当前游戏状态信息数据
    /// </summary>
    /// <returns></returns>
   private SaveGameData CreateSaveObject()
    {
        SaveGameData save = new SaveGameData();
        if (spawn.childCount > 0)
        {
            //如果有敌机就把敌机的位置信息和类型添加到List中
            GameObject[] enemys = new GameObject[spawn.childCount];
            //List<GameObject> enemys = new List<GameObject>();
            for (int i = 0; i < spawn.childCount; i++)
            {
                //enemys.Add(spawn.GetChild(i).gameObject);
                enemys[i] = spawn.GetChild(i).gameObject;
            }
            foreach (GameObject enemyGO in enemys)
            {
                EnemyControl enemyControl = enemyGO.GetComponent<EnemyControl>();                
                save.livingEnemyPostions.Add(enemyGO.transform.localPosition);
                int type = enemyControl.enemyType;
                save.livingEnemyTypes.Add(type);
            }
        }
        //把score,hp和skillCount保存在save对象中
        save.score = score;
        save.hp = player.GetComponent<PlayerHealth>().curHP;
        save.skillCount = player.GetComponent<PlayerControl>().skillCount;
        return save;
    }

    /// <summary>
    /// 通过读档信息将游戏重置为保存游戏时的状态
    /// </summary>
    /// <param name="save"></param>
    private void SetGame(SaveGameData save)
    {
        //GameObject[] enemys = new GameObject[spawn.childCount];
        //通过反序列化的得到的对象中存储的信息在指定位置生成对应类型的敌人
        for (int i = 0; i < save.livingEnemyPostions.Count; i++)
        {
            Vector3 pos = save.livingEnemyPostions[i];
            int type = save.livingEnemyTypes[i];
            GameObject enemy = Instantiate(Resources.Load<GameObject>("prefabs/enemy" + type.ToString()), spawn);
            enemy.transform.localPosition = pos;
        }
        //更新UI显示
        score = save.score;
        txt_Score.text = score.ToString();
        player.GetComponent<PlayerHealth>().curHP = save.hp;
        UpdateHP(player.GetComponent<PlayerHealth>().curHP);
        player.GetComponent<PlayerControl>().skillCount = save.skillCount;
        player.GetComponent<PlayerControl>().UpdateSkillCount();
        UnPause();
    }
    //二进制方法:存档和读档
    private void SaveByBinary()
    {
        //序列化过程——(将SaveGameData对象转化为字节流)
        //创建SaveGameData对象并保存当前游戏状态信息
        SaveGameData save = CreateSaveObject();
        //创建一个二进制格式化程序
        BinaryFormatter bf = new BinaryFormatter();
        //创建一个文件流
        FileStream fileStream = File.Create(path + "/savebyBin.txt");
        //调用二进制格式化程序的序列化方法来序列化save对象  参数:创建的文件流和需要序列化的对象
        bf.Serialize(fileStream, save);
        //关闭流
        fileStream.Close();
        //如果文件存在,则显示保存成功
        if(File.Exists(path+ "/savebyBin.txt"))
        {
            ShowMessage("保存成功");
        }
    }
    private void LoadByBinary()
    {
        if(File.Exists(path + "/savebyBin.txt"))
        {
            //反序列化过程——(将字节流转化为对象)
            //创建一个二进制格式化程序
            BinaryFormatter bf = new BinaryFormatter();
            //打开一个文件流
            FileStream fileStream = File.Open(path + "/savebyBin.txt", FileMode.Open);
            //调用二进制格式化程序的反序列化方法,将文件流转化为对象
            SaveGameData save = (SaveGameData)bf.Deserialize(fileStream);
            //关闭文件流
            fileStream.Close();

SetGame(save);
            ShowMessage("");
        }
        else
        {
            ShowMessage("存档文件不存在");
        }
    }

//Xml:存档和读档
    private void SaveByXml()
    {
        SaveGameData save = CreateSaveObject();
        //创建Xml文件的存储路径
        string filePath = Application.dataPath + "/StreamingFile" + "/savebyXML.txt";
        //创建XML文档实例
        XmlDocument xmlDoc = new XmlDocument();
        //创建root根节点,也就是最上一层节点
        XmlElement root = xmlDoc.CreateElement("save");
        //设置根节点中的值
        root.SetAttribute("name", "saveFile");
        //创建下一层XmlElement节点元素
        //XmlElement enemy;
        //XmlElement enemyPosition;
        //XmlElement enemyType;
        //遍历save中存储的数据,并将数据转换为XML格式
        for (int i = 0; i < save.livingEnemyPostions.Count; i++)
        {
            //创建下一层XmlElement节点元素
            XmlElement enemy = xmlDoc.CreateElement("enemy");
            XmlElement enemyPosition = xmlDoc.CreateElement("enemyPosition");
            //设置节点中的值
            enemyPosition.InnerText = save.livingEnemyPostions[i].ToString();
            XmlElement enemyType = xmlDoc.CreateElement("enemyType");
            enemyType.InnerText = save.livingEnemyTypes[i].ToString();
            //设置节点的层级关系(把节点一层一层的添加至XMLDoc中 ,注意先后顺序,这将是生成XML文件的顺序)
            enemy.AppendChild(enemyPosition);
            enemy.AppendChild(enemyType);
            root.AppendChild(enemy);
        }
        XmlElement score = xmlDoc.CreateElement("score");
        score.InnerText = save.score.ToString();
        root.AppendChild(score);

XmlElement hp = xmlDoc.CreateElement("hp");
        hp.InnerText = save.hp.ToString();
        root.AppendChild(hp);

XmlElement skillCount = xmlDoc.CreateElement("skillCount");
        skillCount.InnerText = save.skillCount.ToString();
        root.AppendChild(skillCount);

xmlDoc.AppendChild(root);
        //把XML文件保存至本地
        xmlDoc.Save(filePath);

if(File.Exists(filePath))
        {
            ShowMessage("保存成功");
        }
    }
    private void LoadByXml()
    {
        string filePath = Application.dataPath + "/StreamingFile" + "/savebyXML.txt";
        if(File.Exists(filePath))
        {
            //加载XML文档
            XmlDocument xmlDoc = new XmlDocument();
            //根据路径将XML读取出来
            xmlDoc.Load(filePath);

SaveGameData save = new SaveGameData();
            //通过节点名称来获取元素,结果为XmlNodeList类型
            //XmlNodeList enemylist = xmlDoc.GetElementsByTagName("enemy");
            
            XmlNodeList enemylist = xmlDoc.SelectNodes("enemy");
            //遍历所有子节点并获得子节点的InnerText值
            if(enemylist.Count>0)
            {
                
                foreach (XmlNode enemy in enemylist)
                {
                    XmlNode enemyPosition = enemy.ChildNodes[0];
                    string posStr = enemyPosition.InnerText;
                    //删除首尾的字符
                    posStr = posStr.TrimStart('[');
                    posStr = posStr.TrimEnd(']');
                    string[] strArray = posStr.Split(',');
                    Vector3 enemyPos;
                    enemyPos.x = float.Parse(strArray[0]);
                    enemyPos.y = float.Parse(strArray[1]);
                    enemyPos.z = float.Parse(strArray[2]);
                    //将的得到的位置信息存储到save中
                    save.livingEnemyPostions.Add(enemyPos);

XmlNode enemyType = enemy.ChildNodes[1];
                    int enemyTypeIndex = int.Parse(enemyType.InnerText);
                    save.livingEnemyTypes.Add(enemyTypeIndex);
                }
            }

//XmlNodeList scoreList = xmlDoc.GetElementsByTagName("score");
            //int score= int.Parse(scoreList[0].InnerText);
            //SelectSingleNode只能寻找单个子节点
            XmlNode scoreNode = xmlDoc.SelectSingleNode("save").SelectSingleNode("score");
            int score = int.Parse(scoreNode.InnerText);
            save.score =score;

XmlNodeList hpList = xmlDoc.GetElementsByTagName("hp");
            int hp = int.Parse(hpList[0].InnerText);
            save.hp = hp;

XmlNodeList skillCountList = xmlDoc.GetElementsByTagName("skillCount");
            int skillCount = int.Parse(skillCountList[0].InnerText);
            save.skillCount = skillCount;

SetGame(save);
            ShowMessage("");
        }
        else
        {
            ShowMessage("存档文件不存在");
        }
    }

//Json:存档和读档
    private void SaveByJson()
    {
        //将save对象转换为Json格式的字符串
        //创建SaveGameData对象并保存当前游戏状态信息
        SaveGameData save = CreateSaveObject();
        string filePath = Application.dataPath + "/StreamingFile" + "/savebyJson.json";
        //方法1:利用第三方LitJson库中的JsonMapper将save对象转换为Json格式的字符串
        //string saveJsonStr = JsonMapper.ToJson(save);
        /*备注
         * 注意::用LitJson调用JsonMapper在进行类转json字符串时,如果类的属性中有json不能识别的数据类型,例如float类型会报错JsonException:
         *原因:用于序列化或者反序列化的数据,其类型必须是下面几种
          LiteJosn的数据类型支持下面几种,否则会报错
        public JsonData(bool boolean);
        public JsonData(double number);
        public JsonData(int number);
        public JsonData(long number);
        public JsonData(object obj);
        public JsonData(string str);
       */
        //方法2:利用系统自带JsonUtility将save对象转换为Json格式的字符串
        string saveJsonStr = JsonUtility.ToJson(save);
        //将字符串写入到文件中进行存储
        //创建StreamWriter并将字符串写入
        StreamWriter sw = new StreamWriter(filePath);
        sw.Write(saveJsonStr);
        //关闭StreamWriter
        sw.Close();
        ShowMessage("保存成功");
    }
    private void LoadByJson()
    {
        string filePath = Application.dataPath + "/StreamingFile" + "/savebyJson.json";
        if (File.Exists(filePath))
        {
            //创建StreamReader用来读取流
            StreamReader sr = new StreamReader(filePath);
            //将读取到的流赋值给Json格式的字符串
            string jsonStr = sr.ReadToEnd();
            //关闭
            sr.Close();

//将字符串jsonStr转换为SaveGameData对象(解析)
            //方法1:利用第三方LitJson库中的JsonMapper解析
            //SaveGameData save = JsonMapper.ToObject<SaveGameData>(jsonStr);
            //方法2:利用系统自带JsonUtility解析
            SaveGameData save =JsonUtility.FromJson<SaveGameData>(jsonStr);
            SetGame(save);

ShowMessage("");
        }
        else
        {
            ShowMessage("存档文件不存在");
        }
    }
}

Unity中游戏的存档与读档相关推荐

  1. Unity游戏存档与读档

    目前unity常见存档和读档有几种方式,也就是常见的存储数据的方式(注意存档和读档都是针对单机游戏而言的,角色信息,道具信息,关卡情况等) Unity存档的方式大概分为这两大类 图片来源自siki学院 ...

  2. Unity中采用JSON进行存档和读档

    在利用JSON做存档和读档之前,首先要在Assets导入一个脚本!这个脚本就是LitJson.dll 然后在Unity中首先创建一个Save脚本,用于保存游戏中相关的信息,在这里我存储的是游戏中怪物的 ...

  3. Unity中游戏存档方式

    ##游戏存档 ####在Unity中游戏存档有如下四种方式: PlayerPrefs c#序列化 XML序列化 Json **原文链接:**http://blog.csdn.net/a23765363 ...

  4. C++ 关于存档与读档

    c++可以用来制作游戏.可是,如果制作的游戏不能存档,那就特别让人心疼了.这种的游戏通常都是辛辛苦苦玩了半天,好不容易打出的记录,程序重启后,一夜回到解放前. 看着宝贵的血汗数据就这样变为0,小凤我决 ...

  5. unity中游戏对象的运动控制方法总结

    1.使用unity中的Standard Assets包中的控制器 这个资源在资源商店中搜Standard Assets就可以下载并导入,在其Characters文件夹中有第一人称和第三人称的控制器,在 ...

  6. Unity中游戏重新加载玩家位置不变的方法

    在玩游戏时,有时候会遇见需要保存玩家位置,这种游戏在做游戏和玩游戏时还是很常见的,那么我们是如何来保存玩家的位置,以做到让玩家下次加载时不会受到位置的丢失.下面是我在游戏中的解决方法. 1.先看下我做 ...

  7. Unity中游戏卡牌滚动效果,EnhanceScrollview(适用于NGUI,UGUI)

    最近项目中的活动面板要做来回滚动卡牌预览效果,感觉自己来写的话,也能写,但是可能会比较耗时,看到Github上有开源的项目,于是就借用了,Github的资源地址是:https://github.com ...

  8. Unity中游戏多种武器旋转方式

    -- 本人学习多种单机游戏的总结笔记 2D中武器跟随鼠标位置旋转 这个一些2D屏幕游戏或者像捕鱼达人也会有这类实现. 第一种代码实现(略微简单,如果能没出错,建议使用这个,如果转化出错可以参考第二种的 ...

  9. Unity中游戏的声音管理

    从来没写过博客,作为一名菜鸟也不知道写些什么.特地偷同事点料/ 源地址:http://www.cnblogs.com/jenke/p/4608422.html using UnityEngine; u ...

  10. SaveLoad--Unity存档读档的学习总结

    存档与读档功能 举例: 传统RPG游戏(仙剑.空之轨迹): 1.角色信息(生命值,等级) 2.道具信息(装备,药品) 3.场景信息(场景名称.角色坐标) 4.事件信息(任务相关) 关卡类游戏:关卡的通 ...

最新文章

  1. 机器人过程自动化的10个秘密
  2. 独热编码(One-Hot)的理解
  3. docker 容器状态 restarting_20分钟极简入门Docker
  4. UNITY 带spriterender的对象导出为prefab时主贴图丢失的BUG
  5. 组件开发之ASP.NET中集成资源文件的服务器端控件开发
  6. 系统开发小结【未完待续】
  7. 基于Dapper的开源Lambda扩展,且支持分库分表自动生成实体之基础介绍
  8. Redis面试复习大纲在手面试不慌,内含福利
  9. UI设计中的黄金分割率,实用案例适合临摹学习
  10. 从ASP.NET Core 3.1中的当前URL获取主机信息
  11. mysql 索引未命中_联合索引命中率问题导致SQL查询效率慢的问题
  12. 关于滤波电容、去耦电容、旁路电容作用
  13. 第1章——操作系统概论
  14. 使用js给数组去重的3种常用方法
  15. 无锁、偏向锁、轻量级锁、重量级锁,完整的锁升级!
  16. 系统管理员在企业中的职业定位及发展方向
  17. PCB制板总结(AD制图)
  18. 如何恢复移动硬盘数据删除的文件
  19. 网络连接里面是空白的没有什么的本地连接、interface:没有注册类别
  20. 信息收集入门面试考题

热门文章

  1. windows win10笔记本如何修改屏幕显示亮度、对比度、色调、饱和度?(英特尔显卡控制中心)
  2. php分页函数怎么看样式,分页样式代码
  3. biostar handbook(十一)|基因组变异的表示形式
  4. 10个小故事,思考大数据
  5. CTF---青青子衿(无C++注册机)
  6. 用python批量修改图片尺寸
  7. 首行缩进字符计算机怎么弄,首行缩进2字符如何设置
  8. 一、Java虚拟机概述与JVM结构
  9. iOS:xcode5 自定义模板
  10. 用python爬取校花网图片