原标题:Unity教程 | 自制简易的游戏存档系统

本文将为大家分享如何在Unity中实现简单的游戏存档系统,其中不会包含太多实际的代码,仅介绍在制作过程中需要考虑与解决的问题。该系统由一个学生团队为他们的首款商业游戏《Spirit》的原型开发,虽然简单,但其功能却很完善,也很容易扩展到其它需要保存状态的游戏中使用。

Unity提供了PlayerPrefs来持久化保存游戏状态与数据,但不支持序列化Transform。C#也有大量的文件IO操作选项,但没有提供自动识别对象的方法。下面为大家介绍如何自定义永久性数据存储系统,首先编写一些简单的可序列化的数据结构来存储基本信息(如Transform等),将每个对象与其ID关联,然后将数据写入文件以供再次读取来恢复游戏状态。

第一步是选择生成对象ID的方法,基于创建时间生成的Hash值就很适合用作ID,但C#已经提供了GUID类可用于生成唯一的字符串:

id = System.Guid.NewGuid().ToString();

下面需要创建一个全局管理器类,来将ID与对应的对象进行关联。这个管理器就是一个单例类,带有几个用于查询键值的容器(C#中可能使用Dictionary结构)。还可以根据实际项目需求在Awake函数中添加一些基本的初始化与设置操作。如果有需要,还可以更新脚本执行顺序以确保管理器要先于ID生成脚本执行。另外,最好添加两个查找函数,一个用于将ID映射到实例ID(用于检查重复),另一个用于将ID直接映射到GameObject(用于加载文件时根据ID查找对象)。

ID脚本需要在对象创建时自动生成ID,并在运行期间保持不变。编写脚本时要注意以下几个关键点:

将ID定义为公共的String类型变了,以便Unity在保存并重新加载场景时,ID可以与对象一起被永久保存。可以利用Unity属性在检视视图中以只读方式显示变量值,以避免被误操作。

如果ID为空或者无效,需要在Awake函数中生成ID并在管理器中注册此ID。

在OnDestroy函数中从管理器中注销,以免后续遍历列表时出现空指针。

可以通过检测对象的实例ID与管理器中“记录”的自定义ID是否一致,来判断键值是否“有效”,以避免出现重复。如果不一致,就重新生成自定义ID。为什么这样呢?如果用ID实例化预制件,或是复制一个带有ID的GameObject,Unity也会复制原有的唯一ID,并且无法在Awake函数或其它地方分辨是否出现重复ID。而如果设置得当,管理器会在文件中记录ID,所以只要在查询函数中查看键值是否有效,就可以解决该问题。

最后,为ID脚本及管理器脚本添加[ExecuteInEditMode]标签,确保在编辑场景时脚本能正常运行。

正确设置好管理器与ID脚本后,只需简单地将ID脚本添加到任意需要识别并加载数据的对象上即可。为了节省空间,建议只为需要动态恢复的对象添加该脚本。

上图使用OGID脚本为对象设置了唯一的ID,Saveable脚本是另一个仅用作保存数据的脚本,它利用[System.Serializable]属性在内部构建数据结构来保存加载对象状态所需的数据,例如位置与方向等。

存储系统的其它部分就比较简单了,使用内置的路径变量与C#的文件函数,就可以轻松实现管理器。保存和加载数据是整个系统中最简单的部分,创建一些自定义类来存储对象的变换信息及其它数据,添加函数从GameObject读取数据或写入数据到GameObject,然后将脚本绑定到任意需要存档的对象上。此时可以选择使用自定义文件格式存储数据或直接使用[System.Serializable]属性进行标记,然后新建可序列化类包含自定义容器数组,在存储函数中复制数据容器的引用,使用下面的代码将数据保存到文件中:

System.IO.Stream s = new System.IO.FileStream(filepath, System.IO.FileMode.Create, System.IO.FileAccess.Write);

System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

formatter.Serialize(s, saveGame);

s.Close();

可以选择二进制或XML序列化对数据进行编码。但二者都有自己的长短处:

二进制:文本文件不易阅读与编辑(对开发者不友好但能够预防玩家作弊),读写速度略快,相比之下数据较易损坏。

XML:文本文件便于阅读与编辑,读写速度稍慢,文件出错时较易修复。

本例中采用的是二进制文件,因为它可以将对象保存为二进制字符串,或将二进制字符串还原为对象。

System.IO.Stream s = new System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read);

System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

SaveGame saveGame = (SaveGame) formatter.Deserialize(s);

s.Close();

对保存的文件进行反序列化后,可以调用自定义函数将文件中的数据还原为GameObject(通过ID查找)。如果构建文件时遍历带有ID的对象,新对象会被自动保存到文件中。还可以修改代码来保存其它数据类型或调整文件管理器。

使用Unity自带的UGUI可以很方便的实现游戏截图、调整UI布局并创建存档菜单,并从存档中恢复游戏数据。这里存档信息显示了玩家名称与时间戳,以及玩家存档时的游戏截图。

结语

本文介绍的只是Unity中实现文件存档的其中一点内容,文中的实现方式不一定适用于所有游戏,但其中介绍的思路与关键注意事项可以供大部分游戏参考。希望本文对大家有帮助,我们还会分享一些实际的游戏开发经验在Unity官方中文社区(unitychina.cn),请保持关注!返回搜狐,查看更多

责任编辑:

unity 编辑器存档_Unity教程 | 自制简易的游戏存档系统相关推荐

  1. Unity编辑器类中文教程汇总-Chinar(一个默默无闻分享知识的人)

    Chinar blog :www.chinar.xin Unity 编辑器资料汇总 本文提供全流程,中文翻译 统计我所有编辑器 Unity Editor 教程资料,便于查询学习 为初学者节省宝贵的时间 ...

  2. vb.net 教程 4-9 二进制文件读写 游戏存档修改器

    版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的. 前几节学习了读取二进制文件,实际写入二进制文件操作差不多,本节制作一个简单的游戏存档修改器,来说 ...

  3. html游戏存档在哪里,steam游戏存档位置在哪里-查找steam游戏存档位置的方法 - 河东软件园...

    Steam是一款发型时间比较久的游戏平台,因此在这个平台中我们可以下载到小时候的游戏怀旧,也可以感受到目前最先进的游戏程序.在使用这个软件的时候我们会将自己的游戏进度存档,一般很多的单机游戏是不能一次 ...

  4. Qt自制简易好看的日志系统

    简介 预览 原理 html格式的log Qt的log系统 融合 文件句柄复用 多线程测试 github仓库链接 简介 一个完善的软件工程,自然是少不了log系统的. 这次涛哥教大家,用最少的代码做一个 ...

  5. android township 游戏存档备份,游戏闪退存档全没了?不要怕,可以这样备份与还原游戏存档!...

    不少朋友可能有过这样的苦恼,好不容易在第三方商店下到好玩的修改版游戏,结果玩了一阵子突然提示证书失效,游戏闪退打不开了,只能卸载重装.可是卸载后,游戏之前的存档就没了,又得耗费大量的时间重头再来,这样 ...

  6. 运用 iMazing备份《保卫萝卜》游戏存档

    游戏存档,是一种记录游戏状态.进度的文件.比如,在玩一些过关类游戏时,游戏存档能记录已经通过的关卡,在下一次打开游戏时,会直接调取存档,跳过已经通关的关卡. 当我们要更换设备玩游戏时,就可以通过备份游 ...

  7. unity2d游戏开发系列教程:二、新建工程并熟悉Unity编辑器常用功能

    目录 unity2d游戏开发系列教程:一.环境安装 第一步.打开项目 耐心等待一小会 工程界面 第二步.创建第一个场景(第一关)进行试玩 点击图中标号1的运行按钮,即可简单试玩感受,操作如下 移动A, ...

  8. Unity编辑器AssetDatabase函数API用法中文详解-Chinar教程

    Chinar blog :www.chinar.xin AssetDatabase 函数用法汇总 本文提供全流程,中文翻译 助力快速理解 AssetDatabase API 用法 为初学者节省宝贵的时 ...

  9. 【Unity】简易游戏存档

    前言 游戏存档的功能和重要性不需要过多叙述,实现的方法也很多样. 对于Unity来讲,最简单的就是用PlayerPrefs来搞.但是就因为简单,所以能实现的功能也比较少.目前PlayerPrefs存些 ...

最新文章

  1. sql针对某字段去重查询_sql的简单查询
  2. 【学习笔记】mybatis自定义插件案例代码
  3. 网站快速成型_我的老板对快速成型有什么期望?
  4. BZOJ4590: [Shoi2015]自动刷题机
  5. jboss项目导入idea_JBoss BPM Suite快速指南–将外部数据模型导入BPM项目
  6. MyBatis-Plus_Condition作用
  7. 利用dxflib读写cad文件
  8. http://item.jd.com/1275996920.html
  9. [20171120]bash使用here documents的细节
  10. html加载本地pdf,WkHTMLtoPDF不加载本地CSS和图像
  11. 人民币对美元汇率中间价报6.7592元 上调23个基点
  12. SQL 获取本周日期
  13. 微信,QQ头像专属制作
  14. 洪水填充算法_优化洪水相似算法(渗流理论)
  15. java 程序暂停_java程序运行过程中如何暂停,恢复?
  16. 基于移动终端的大学生心理健康交互管理系统的研究与设计
  17. 西瓜书------前两章
  18. ubuntu:防火墙配置详细讲解(全)
  19. 三人成虎,概率却不足十分之五?几个贝叶斯推理故事的分享
  20. RuntimeError: applying transform <monai.transforms.croppad.dictionary.RandCropByPosNegLabeld object

热门文章

  1. 遗传算法解决旅行商问题(TSP)
  2. 使用Windbg找出死锁,解决生产环境中运行的软件不响应请求的问题
  3. 【异常】org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=36, exceptions:
  4. Too many open files错误与解决方法
  5. windows下本地或者远程连接MYSQL数据库,报1130错误的解决方法
  6. JUnit报initializationError的解决方法
  7. zookeeper 启动显示started,jps查看进程却没有,解决方法
  8. 原生js实现Ajax,JSONP
  9. 检查mysql当前状态
  10. Android Studio2.2.3 使用教程-入门篇