unity -- 存档与读档

不同于unity自带的PlayerPrefs的字典式存储,本文主要说明三种文件存储的具体实现方式(项目原型基于siki案例–存档与读档,类似于打砖块的小游戏),即:二进制方法、XML以及JSON。

三种存储方式的对比

一、二进制方式存储
存储简单,但基本没有可读性。
二、XML
可读性强,但文件庞大,冗余信息多。
三、JSON
数据格式比较简单,易于读写,但是不直观,可读性比XML差。

三种方式存储的预先准备

首先得创建一个Save类存储相关数据并标记为可序列化,本例具体代码如下:
(新建一个C#脚本,将继承类删除即可开始编写Save类,Save类用于存储需要存储的数据)

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

[System.Serializable]
public class Save
{
    public List<int> activeMonsterPosition = new List<int>();
    public List<int> activeMonsterType = new List<int>();

    public int shootNum;
    public int score;
}

一、二进制方式的存档与读档

需要添加两个命名空间
1、using System.IO; //用于文件的读写
2、using System.Runtime.Serialization.Formatters.Binary; //用于创建二进制格式化程序

存档代码如下:

private void SaveByBin()
    {
        //创建存储类对象
        Save save = CreateSaveGo();
        //创建二进制格式化程序
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        //创建文件流(create参数为存储路径,注意路径前的"/"以及标明文件名称和格式)
        FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile" + "/byBin.txt");
        //用二进制格式化程序的序列化方法来序列化save对象 
        binaryFormatter.Serialize(fileStream, save);
        //关闭文件流
        fileStream.Close();
    }

读档代码如下:

private void LoadByBin()
    {
        if (File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt")) //检测是否存在读档信息
        {
            //创建二进制格式化程序
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            //创建文件流打开读档信息
            FileStream fileStream = File.Open(Application.dataPath + "/StreamingFile" + "/byBin.txt", FileMode.Open);
            //调用二进制格式化程序的反序列化方法,将文件流转化为Save对象
            Save save = (Save)binaryFormatter.Deserialize(fileStream); //返回值为object类型,需要强制转换为需要的类型
            //关闭文件流
            fileStream.Close();
            //将读取到的游戏属性设置为游戏当前属性(根据游戏需要自行编写)
            SetGame(save);
        }
    }

二、JSON方式的存档与读档

需要添加两个命名空间
1、using System.IO; //用于文件的读写
2、using LitJson;//用于json数据的转换(需要自行导入LitJson.dll文件)

存档代码如下:

 private void SaveByJson()
    {
        //保存当前游戏状态,创建save对象(CreateSaveGo函数根据保存数据需要编写)
        Save save = CreateSaveGo();
        //保存文件路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";//文件类型可以时.json也可以是.txt
        //通过JsonMapper.ToJson方法将save对象中的数据转化为json字符串保存下来
        string saveJsonStr = JsonMapper.ToJson(save);//using LitJson;
        //创建StreamWriter写入流(注意需要一个string参数代表路径)
        StreamWriter streamWriter = new StreamWriter(filePath);
        //将转化后的字符串写入目标文件
        streamWriter.Write(saveJsonStr);
        //关闭StreamWriter
        streamWriter.Close();
    }

读档代码如下:

读档代码逻辑就是存档的反向逻辑。

 private void LoadByJson()
    { 
        //string保存路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";
        //创建文件读取流(注意new对象时需要string类型参数代表路径)
        StreamReader streamReader = new StreamReader(filePath);
        //读取文件中所有的字符串(ReadToEnd代表读取到最后,即读取所有)
        string jsonStr = streamReader.ReadToEnd();
        //将读取到的字符串通过JsonMapper.ToObject转换为Save类型的对象(JsonMapper.ToObject<类型>(路径);)
        Save save = JsonMapper.ToObject<Save>(jsonStr);//using LitJson;
        //设置游戏状态,根据游戏需要自行编写
        SetGame(save);
    }

三、XML方式的存档与读档

需要添加两个命名空间
1、using System.IO; //用于文件的读写
2、using System.Xml; //用于创建XML文档

存档代码如下:

xml虽然有系统自带的类库,但是所有的结点需要自己添加,相比其它两种方法要麻烦,但过程逻辑不复杂,了解自己编写的Save类即可轻松编写。
总体思路是 XmlDocument ( root ( target( …… ) – shootNum – score ) ) 【括号代表层级关系,"–"代表同级】

 private void SaveByXml()
    {
        //保存当前游戏状态,创建save对象(CreateSaveGo函数根据保存数据需要编写)
        Save save = CreateSaveGo();
        //保存文件路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byXml.txt";
        //总思路:XmlDocument (    root (  target(  ……  ) -- shootNum -- score  )   )
        //括号代表层级关系,"--"代表同级
        //创建Xml文档
        XmlDocument xmlDocument = new XmlDocument();
        //创建xml根节点
        XmlElement root = xmlDocument.CreateElement("save");
        //设置根结点的值(名字的名称 ,名字)
        root.SetAttribute("name", "saveFile1");

        //创建xml元素(每个元素都包含名称 和 内部包含的值)
        XmlElement target;
        XmlElement targetPosition;
        XmlElement monsterType;

        //循环赋值
        for (int i = 0; i < save.activeMonsterPosition.Count; i++)
        {
            //设置xml元素的名称
            target = xmlDocument.CreateElement("target");
            targetPosition = xmlDocument.CreateElement("targetPosition");
            monsterType = xmlDocument.CreateElement("monsterType");

            //设置xml元素内部包含的值(即存储的字符串)
            targetPosition.InnerText = save.activeMonsterPosition[i].ToString();
            monsterType.InnerText = save.activeMonsterType[i].ToString();

            //设置层级关系(root -> target -> ( targetPosition -- monsterType ) )
            root.AppendChild(target);
            target.AppendChild(targetPosition);
            target.AppendChild(monsterType);
        }

        //创建xml元素并设置名称
        XmlElement shootNum = xmlDocument.CreateElement("shootNum");
        XmlElement score = xmlDocument.CreateElement("score");
        //设置xml元素内部包含的值(即存储的字符串)
        shootNum.InnerText = save.shootNum.ToString();
        score.InnerText = save.score.ToString();

        //设置层级关系(xmlDocument -> root -> ( shootNum -- score ))
        root.AppendChild(shootNum);
        root.AppendChild(score);
        xmlDocument.AppendChild(root);

        //将xml文档保存到指定路径
        xmlDocument.Save(filePath);
    }

读档代码如下:

总体思路: XmlDocument -> XmlNodeList -> XmlNode ( -> XmlNode .ChildNodes[……]) -> InnerText

  private void LoadByXml()
    {
        //保存文件路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byXml.txt";
        //当保存文件存在时进行读档
        if (File.Exists(filePath))
        {
            //创建Save保存类对象
            Save save = new Save();
            //创建xml文档
            XmlDocument xmlDocument = new XmlDocument();
            //加载指定路径的xml文档
            xmlDocument.Load(filePath);

            //通过结点名来获取结点,返回值为XmlNodeList类型(存储XmlNode的List)
            XmlNodeList targets = xmlDocument.GetElementsByTagName("target");
            //当存在target时进行遍历
            if (targets.Count != 0)
            {
                //foreach遍历获得的list -- targets
                foreach (XmlNode target in targets)
                {
                    //获得list中的结点(对应位置参考存储时的位置)
                    XmlNode targetPosition = target.ChildNodes[0];
                    //直接获得该结点的innerText值(注意转化类型)
                    int targetPositionIndex = int.Parse(targetPosition.InnerText);
                    //给save对象赋值
                    save.activeMonsterPosition.Add(targetPositionIndex);

                    //获得list中的结点(对应位置参考存储时的位置)
                    XmlNode monsterType = target.ChildNodes[1];
                    //直接获得该结点的innerText值(注意转化类型)
                    int monsterTypeIndex = int.Parse(monsterType.InnerText);
                    //给save对象赋值
                    save.activeMonsterType.Add(monsterTypeIndex);
                }
            }

            //通过结点名来获取结点,返回值为XmlNodeList类型(存储XmlNode的List)
            XmlNodeList shootNumCount = xmlDocument.GetElementsByTagName("shootNum");
            //注意当XmlNodeList中的XmlNode没有子节点的时候可以直接获得XmlNode的InnerText
            int shootNum = int.Parse(shootNumCount[0].InnerText);
            //给save对象赋值
            save.shootNum = shootNum;

            //通过结点名来获取结点,返回值为XmlNodeList类型(存储XmlNode的List)
            XmlNodeList scoreCount = xmlDocument.GetElementsByTagName("score");
            //注意当XmlNodeList中的XmlNode没有子节点的时候可以直接获得XmlNode的InnerText
            int score = int.Parse(scoreCount[0].InnerText);
            //给save对象赋值
            save.score = score;

            //将读取到的游戏属性设置为游戏当前属性(根据游戏需要自行编写)
            SetGame(save);
        }
    }

本例中的CreateSaveGo()函数和SetGame()函数

CreateSaveGo():

   private Save CreateSaveGo()
    {
        Save save = new Save();
        //遍历每一个目前存活的目标,逐个保存
        foreach (GameObject targetGo in targetGos)
        {
            TargetManager targetManager = targetGo.GetComponent<TargetManager>();
            if (targetManager.activeMonster != null)
            {
                save.activeMonsterPosition.Add(targetManager.targetPosition);
                int type = targetManager.activeMonster.GetComponent<MonsterManager>().monsterType;
                save.activeMonsterType.Add(type);
            }
        }
        //保存UI属性
        save.shootNum = UIManager._instance.shootNum;
		save.score = UIManager._instance.score;
        return save;
    }

SetGame():

    private void SetGame(Save save)
    {
        foreach (GameObject target in targetGos)
        {
            target.GetComponent<TargetManager>().UpdateMonster();//先将所有怪物清空
        }
        for (int i = 0; i < save.activeMonsterPosition.Count; i++)
        {
            int position = save.activeMonsterPosition[i];
            int type = save.activeMonsterType[i];

            targetGos[position].GetComponent<TargetManager>().ActiveMonsterByType(type);//激活指定位置的怪物
        }
        //设置UI
        UIManager._instance.shootNum = save.shootNum;
        UIManager._instance.score = save.score;

        DePause();//取消暂停状态、关闭菜单
    }
  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Spine-Unity-3.8-2021是一个Spine动画的Unity插件,可以让开发人员在游戏开发过程中轻松实现Spine动画。这个插件包含了最新的Spine Runtime库,可以让开发人员在使用Spine动画时获得更好的性能和可靠性。此版本还提供了对C# 9的支持,并修复了在以前的Spine Unity版本中存在的一些问题和异常。此外,此版本也提供了一些改进和优化,能够让开发人员更快速、更容易地实现动画效果并提高游戏的运行效率。总的来说,Spine-Unity-3.8-2021是一个值得开发人员关注和使用的插件,它可以帮助游戏开发人员更好地实现动画效果,提升游戏的质量和用户体验。 ### 回答2: spine-unity-3.8-2021是一款在Unity中使用的Spine动画软件包。Spine是一种2D骨骼动画软件,它可以帮助制作2D游戏中的角色动画。与传统的逐帧动画相比,Spine骨骼动画具有更高的效率和更好的表现效果。 spine-unity-3.8-2021是Spine运行在Unity中的版本。它提供了一个简便的方式来将Spine制作的动画集成到Unity游戏中。spine-unity-3.8-2021拥有许多实用的功能,例如动画的播放、循环、暂停和停止等。 此外,spine-unity-3.8-2021还支持动画的混合、遮罩、缩放等高级特性。这些功能可以大大提升2D游戏的动画表现效果。 总之,spine-unity-3.8-2021是一款强大的Spine骨骼动画软件包,它可以帮助Unity开发者更方便、更高效地制作2D游戏中的动画效果。 ### 回答3: Spine-Unity-3.8-2021是Spine动画引擎的一个版本,其主要特点是可以与Unity引擎无缝集成,提供了高效、灵活、可定制的动画解决方案。此版本相比之前版本,主要增加了一些新功能和改进,如支持GPU动画混合、2D环境的自适应、高效的顶点色边框渲染、Spine Atlas纹理集加载、支持Mecanim(动画过渡和状态机和蒙太奇)和 IK姿势、环境光遮蔽、大量优化和 bug 修复等。除此之外,它还易于使用和实现,并具有快速迭代的能力,可以让开发者轻松创建精美的动画效果,提升游戏的用户体验。该版本是Spine引擎的主要升级版本之一,同时也体现出Spine开发团队对于产品需求和用户反馈的重视和努力,不仅提高了动画制作领域的生产力和创造力,也为游戏行业推陈出新提供了有力支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值