保存游戏系统的总览 (Save Game System Overview)

总览 (Overview)

"保存游戏系统"中包含的游戏保存和加载功能,是专门为使用这个插件而设计的。你可以对“保存游戏系统”进行扩展、修改或忽略它,或者完全替换它。

目录

[TOC]


保存游戏的文件位置 (Saved Game File Location)

“保存游戏的文件”也就是“存档”。

在默认情况下,“存档”文件位于“Application.persistDataPath”属性指定的文件夹中。有关“Application.persistDataPath”的详细信息,请参见Unity文档

例如,在Windows 7系统上,这个文件夹的路径为:

C:\Users\[USERNAME]\AppData\LocalLow\While Fun Games\First Person Exploration Kit Demo v2\


请注意,“While Fun Games”文件夹和“First Person Exploration Kit Demo v2”文件夹的路径地址,是与Unity内部的“Player Settings”中的某些设置相关联的。

现在“Player Settings”中的设置是用于本插件中示例场景的默认设置 。要更改这些设置,请打开上方菜单栏中的File -> Build Settings -> Player Settings,然后更改Company Name选项和Product Name选项,以满足你的需求。

当你修改完毕后,“保存游戏系统”将继续工作,无需任何进一步的更改。

SavedGameLocation.jpg


“保存游戏系统”使用了几个关键的文件/组:

文件名 文件名的中文翻译———————— 说明
options.dat 选项 这个文件中储存了:与游戏选项(比如:鼠标的灵敏度)相关的所有数据。
core.dat 核心 这个文件中储存了:一小组核心的数据。(例如:last level saved等)
inventory.dat 库存 这个文件中储存了:有关玩家库存的详细信息。例如:物品、数量、笔记、日志等。
player.dat 玩家 这个文件中储存了:玩家的核心数据。例如:玩家的位置、玩家视线方向等。
level_N.dat 关卡_N 这个文件中储存了:每个level(Level应该是场景的意思吧)中的对象数据。(其中N是场景的索引,例如“level_1.dat”)。
请注意,只有这类文件同时具有“full(全部)”和“auto(自动)”保存的功能。


结构 (Architecture)

下面是一个高级架构图,显示了系统的结构。

ArchitectureSaveLoad.jpg


在默认情况下,“保存游戏系统”使用的是C#对象序列化和二进制格式化,将数据写入文件。你也可以将保存数据的方式,替换为JSON、XML、纯文本或您最熟悉的任何内容。

使用C#原生的序列化的原因是,它是C#原生的功能,可以直接在Unity中使用。还有一个原因是,虽然有很多JSON库也可以用,但由于这些库都是第三方的库,考虑到许可证和维护的问题,因此没有把这些库包含在本插件中。所以才使用了C#原生的序列化。


如果你要替换"保存游戏系统"的核心,你只需要更改系统中,将数据写入文件部分的代码。这个部分的代码都包含在“FPESaveLoadManager”类中。

在“FPESaveLoadManager”类中,有一个“FPESaveLoadLogic”类型的变量。这个“FPESaveLoadLogic”类型的变量,用于与“FPESaveDataTypes”脚本进行通信(也就是说,让游戏对象和文件之间进行通信)。

例如,如果你想要将保存文件的方式替换为JSON,那么只需要在“FPESaveLoadLogic”脚本中,更改各种数据类型写入JSON的方式。其余的逻辑应该能够“正常工作”,因为它不关心怎样从文件创建类型,只需要数据有效就行。


最后,这个“保存游戏系统”只在Windows系统的电脑上进行了测试。如果你要使用到其他平台(例如移动设备、控制台等),可能需要你进行额外的设计和测试。

注意:
这个系统仅作为一个“让你为你的整个游戏创建更大的保存系统”的指南。
这个系统可以让你保存和加载First Person Exploration Kit插件中的核心数据和功能。
并提供了指导和示例的代码,以帮助你保存/读取:不在此插件范围内的自定义类型和对象。


公开的接口 (Public Interfaces)

在这里,“公开的接口”应该是“公开的函数”的意思。

"游戏的保存和加载"功能,由FPESaveLoadManager预制体进行处理。如果你的场景里有一个FPECore预制体,那么将会在场景运行时,自动为你创建一个FPESaveLoadManager预制体。

用于保存和加载的所有公开函数,都位于FPESaveLoadManager.cs脚本的“public_interface”的代码区域中。

这些公开函数是:

函数名 函数名的中文翻译———————— 说明
SaveGame() 保存游戏 调用这个方法,将执行“全部保存(full save)”的功能。
这个方法与“Save Game(保存游戏)”菜单选项,一起使用。
(这个方法可以很方便的让“保存游戏”功能,与“保存游戏(Save Game)”菜单中的选项,关联在一起。)
LoadGame() 读取游戏 调用这个方法,将执行“全部加载(full load)”的功能。
这个方法与“Load Game(加载游戏)”菜单选项,一起使用。
ChangeSceneToAndAutoSave() 更改场景并自动保存 更改场景(跳转场景),并自动保存当前场景的状态。
ChangeSceneToNoSave() 更改场景但不保存 更改场景,并且不会自动保存当前场景的状态。
SavedGameExists() 是否存在游戏存档 检查是否存在已保存的游戏存档。(玩家是否拥有一个游戏存档?)
然后我们可以刷新菜单的UI(例如,如果玩家还没有游戏存档,则应该禁用(或者隐藏)“加载游戏”的按钮)。
StartANewGame() 开始一个新游戏 删除现有的“游戏存档”文件,并重置基础数据。
SaveGameOptions() 保存游戏的选项 根据FPEInputManager脚本的当前状态,保存游戏的选项。
LoadGameOptions() 读取游戏的选项 加载游戏选项的数据,并更新FPEInputManager的状态(这样可以把读取到的选项数据,反映出来)。

这些函数可以根据你的需要,在你的游戏菜单或快捷键中进行调用。


保存的对象数据 (Saved Object Data)

本插件中,核心的“可交互物”、玩家的库存、玩家的状态,以及游戏的选项数据,都可以被保存和加载。

如果你要保存和加载自定义的数据类型,可以这样做:将你自定义的类型添加到“FPESaveDataTypes”脚本文件,然后再添加一些逻辑,让系统可以“收集”和“恢复”这个数据类型的数据到场景中。在FPESaveDataTypes脚本中,有一个叫做“CUSTOM_TYPES”的代码区域,就是用于实现这个功能的。

本插件自带的基本类型,可以允许你保存和加载的Unity的数据类型,例如:Vector2、Vector3、Transform和Quaternions等。你可以组合这些“本插件自带的基本类型”,来保存基本的Unity数据,或者其他你自定义的类型数据。

这个插件中自带的“可被保存”的类型(比如:“可交互物”),可以作为你创建自定义的“可保存的数据类型”的参考。

除了下面列出的“这个插件中自带的类型”之外,你还可以创建自己的custom data types(自定义的数据类型)。


玩家库存 (Player Inventory)

库存物品类型的对象 (Inventory Item Type Objects)

所有的“库存物品”都必须位于Resources/RegioryItems文件夹中。


世界对象 (World Objects)

可拾取道具类型的对象 (Pickup Type Objects)

场景中的所有“库存物品”对象和“可拾取道具”对象,都必须是预制体。如果这些对象不是预制体,那么“保存游戏系统”将无法从Resources(资源)文件夹中,加载这些对象。


命名规范 (Naming Conventions)

保存“可拾取道具”类型的对象时,对象名字中第一个空格(或左括号之前)的文字,将会被认为是预制体的名称。

例如,场景中名为“demoSoup”、“demoSoup (1)”、“demoSoup(Clone)”和“demoSoup special2_FINAL”的“可拾取道具”对象,都将被视为是来自“demoSoup”预制体的。但是,名为“demoSoupIntro”的对象,将被认为是来自叫做“demoSoupIntro”的预制体。

注意:
要将这中“分隔字符”自定义为空格(或者左括号)以外的其他字符,
请参阅叫做FPEObjectTypeLookup脚本中的“PickupPrefabDelimiter”变量(char类型的数组)。


所有“可拾取道具”类型的对象,必须位于“Resources/Pickups”文件夹中。例如,场景中的“demoSoup”对象的预制体,位于“Resources/Pickups/demoSoup.prefab”中。

所有“库存”类型的对象,必须位于“Resources/InventoryItems”下的路径中。 例如,场景中的“demoApple”对象的预制体,位于'Resources/InventoryItems/demoApple.prefab'中。

注意:
要自定义资源(Resource)的路径,请参阅FPEObjectTypeLookup类。


所有“库存”类型的预制体,必须在FPEObjectTypeLookup脚本中注册查找条目(lookup entry)。如果未提供任何条目,则不能保存和加载这个类型的对象,并会打印错误。

“注册查找条目”就是“新注册一个库存物品”的意思。
(比如,你想做一个新的库存物品,这个可交互物是一瓶可乐。
那么你需要把这个可乐注册到系统中,才能让这瓶可乐可以被放入库存中。)

下面是一个“添加查找条目”的示例:

inventoryItemsLookup.Add(FPEInventoryManagerScript.eInventoryItems.APPLE, "demoApple");

这行代码可以添加一个查找条目:我们在系统中新注册一个APPLE类型的“可交互物”,APPLE类型的对象的预制体名字叫做“demoApple”。

这意味着当加载游戏后,并且遇到类型为“APPLE”的“可交互物”时,将实例化“Resources/InventoryItems/demoApple.prefab”预制体。

你必须对每个“库存物品”类型都注册查找条目,才能使“保存游戏系统”正常工作。


FPEInteractableDockScript类型 (FPEInteractableDockScript Type)

玩家停靠的状态,会按照你的预期进行保存和加载。

但是,如果“停靠点”类型的对象具有复杂的状态机(state machine)(应该是说如果具有动画状态机的话),那么必须单独保存这些状态机的状态。

例如,如果玩家坐在电视机前面,并且玩家保存了游戏。那么当玩家加载游戏时,玩家会仍然坐在电视机前。但是,如果你的停靠点涉及到一些复杂的动画状态,比如电视机上会播放几个短片,那么你必须保存这些动画状态。

这里有3种简单的方法,帮你实现这个功能:

  1. 将复杂的状态机设置为保存游戏逻辑(save game logic)的一部分,并在加载游戏时恢复它。
  2. 不要保存它,在玩家重新加载游戏时,让玩家自己重新去激活它。
  3. 添加一些逻辑,不允许在与复杂的状态机进行交互时保存游戏


FPEDoor类型 (FPEDoor Type)

注意:
FPEDoor类对象的示例,
请参阅\Prefabs\DemoPrefabs\DoorsAndDrawers\文件夹中“门”的预制体。

“门”要保存和读取的数据有:“内部的锁”的状态、“外部的锁”的状态,以及“门把手的交互字符串”。这些数据都是在内部进行管理的。只要每扇门都有唯一的名字,你就不用操心了,“门”就会正常的被保存和读取。

这个插件中,配有“滑动门”和两种类型的“摆动门”。如果你想创建一个其他类型的门,你可以扩展FPEDoor类。


FPEDrawer类型 (FPEDrawer Type)

注意:
有关FPEDrawer类对象的示例,
请参阅\Prefabs\DemoPrefabs\DoorsAndDrawers\文件夹中的“抽屉”的预置体。
(例如demoBasicDesk.prefab)。

“抽屉”要保存和读取的数据有:“内部的锁”的状态、“外部的锁”的状态,以及“抽屉把手的交互字符串”。这些数据都是在内部进行管理的。只要每个抽屉都有唯一的名字,你就不用操心了,“抽屉”就能被正常的保存和读取。

如果你想创建一个其他类型的抽屉,你可以扩展FPEDrawer 类。


FPEInteractableActivateScript类型 (FPEInteractableActivateScript Type)

激活事件 (Activate Events)

对于每个“可激活物”类型的对象,它们身上的FPEInteractableActivateScript脚本中,都有一个“Fire Toggle Events On Load Game(在加载游戏时,触发Toggle事件)”选项。如果将此选项设置为true,则在加载游戏时触发Toggle事件,从而恢复保存时的开关状态。例如:玩家打开电灯的开关,然后保存游戏。如果这个选项的值为true,那么会在加载游戏时,再次触发“Toggle事件”,从而自动打开电灯的开关。

对于其他的“可激活物”类型的对象(比如:门),可以将这个选项设置为false。这将允许在保存游戏时打开的门,在加载游戏时仍处于打开状态。


FPEEventTrigger类型 (FPEEventTrigger Type)

触发器的事件 (Trigger Events)

“事件触发器”是特殊的,因为它们几乎可以做任何事情。也许最特别的是它们可以包含逻辑循环。

例如,事件触发器A可以撤销自身的“部署”,而事件触发器B可以撤销自身和事件触发器A的的“部署”。如果你想在保存状态时,保存“事件触发器的事件”的顺序,这实际上是不可行的。

因此,这些“事件触发器”的状态被保存,但它们的事件的后果却没有。也就是说,“事件触发器”的“部署(Armed)”和“撤销(Tripped)”状态将被保存。但对对象进行的启用、禁用、销毁等操作,是不会被保存的。

对于复杂的“事件触发器的事件”(例如:序列化脚本、过场动画、大型组合等),建议将其作为较大的事件,来单独进行跟踪。


例如:

  1. 事件触发器A被激活时:设置事件触发器A为“撤销”状态,并且设置事件触发器B为“部署”状态
  2. 事件触发器B被激活时:设置为事件触发器为“撤销”状态,并升起一面墙
  3. “保存系统”将跟踪事件触发器A和事件触发器B的状态,现在A和B都被激活了,而且都是“撤销”状态
  4. 现在,你需要额外做一些工作,以保存在“玩家保存游戏”时,升起的那面墙的状态
注意:
对于特殊物品的实例化(生成)或其他的内容,建议将这些需要生成的物品,做成是可保存的类型。
例如,如果有一个事件触发器在激活时,生成了一个demoApple,
因为这个demoApple是一个Pickup类型的对象,所以会被保存系统自动的保存和加载。


音频日志和附加笔记 (Audio Diaries and Attached Notes)

当玩家播放音频日志或阅读笔记时,相应的信息(例如:文本、声音)将作为音频日志和笔记条目,添加到库存中。

当游戏被保存时(使用完全保存(full)或自动保存(auto)),将会保存这些日志和笔记条目的对象的“收集(collected)”状态。

例如,如果玩家“读取/收集”场景1中的“笔记A”,然后玩家保存了游戏。再读取游戏时,他们就不能再次收集该笔记了,因为这个笔记已经在库存中了。


FPEGenericSaveableGameObject类型 (FPEGenericSaveableGameObject Type)

这个插件中还包含了一个叫做“FPEGenericSaveableGameObject”的脚本,我们称之为“通用的保存数据的类型”。这个脚本可以用于“其他的一般的”对象(这个脚本非常的通用),让这些对象可以支持数据的保存和加载。这个脚本会用到“FPEGenericSaveableInterface”接口和“FPEGenericObjectSaveData”数据类型。

results matching ""

    No results matching ""