
这是我们完成关卡序列化、场景存档功能后进一步的功能。现在我们的游戏还是关卡制关卡结束后场景全部被销毁。如果一个关卡做不长就切场景然后关卡结算感觉特别敷衍。把游戏做成一个个关卡关卡结束后只有奖励等信息保存下来是比较容易的。我们要允许玩家在一个关卡里在不同地图里传送传送过去后里面有道具、NPC、敌人玩家改变其中一些对象的状态可以保存下来。箱庭多场景游戏中玩家可以在任意时刻通过传送点到其他场景的对应传送点。需要一直维护所有场景当前的状态包括里面的道具、NPC、敌人、任务点分布。每个场景一个文件。玩家的操作比如开启了任务会对未开启的场景产生影响可能是放置NPC、敌人如果是任务目的地在那个场景先提示玩家传送到该场景然后标记目的地。地图系统完善在此之前需要先升级一下小地图让它显示锚点位置而且小地图显示不了全地图需要一个大地图界面。小地图应该有一个函数接收一个大世界位置和使用的图标把它显示到小地图上。那么小地图控制器维护一个MarkBehavior列表里面能拿到物体的世界位置换算后显示到小地图。考虑到被显示对象可能在任何时候出现、消失应该让对象把自己注册到列表。而不是让小地图控制器去收集。小地图还要维护一个在显示的Image列表物体出现、消失Image也显示、消失。传送系统需要先做传送系统传送点可以设计为通往固定的传送点或者可选通往哪个传送点。对于海岛地图传送点是码头、船那么应该可以任意选其他地图的任意锚点。那么还要设计一个界面让玩家选去哪个岛的哪个锚点。现在大地图已经可以显示锚点鼠标放上去显示标记的描述。我们设想的传送界面是和锚点交互后出现地图列表点击后显示该场景大地图上面有锚点标记点击后弹出对话框是否去往该锚点现在按M的大地图是鼠标进入标记后显示描述。我们希望复用这个UI不同的就是标记的行为。那么首先我们把鼠标进入、鼠标离开、鼠标点击里面弄成执行委托。现在锚点执行交互弹出地图列表点击选项弹出大地图此时大地图的模式是可选锚点。不对此时显示的大地图不是当前所在的地图而是没有加载的它的锚点物体也没有加载而是在terrain预制体下现在的MarkBehavior无法用于显示未加载场景的物体到地图上。到了这一步我们不能再把锚点作为terrain的子物体了锚点数据需要放到地图配置里面。然后我们现在地图配置用的总表一个地图的配置数据量开始大了应该拆分成分表了。锚点管理工作流还需要指定锚点管理的整条工作流在场景里摆好锚点由一个工具可能是编辑器窗口或MonoBehavior检查器收集锚点数据找到当前场景的配置反序列化把锚点数据写入保存。进入场景时根据场景配置生成锚点。之后我们再做输入一个场景配置生成大地图和上面的锚点标记功能。不在那之前我们先把现在的场景总配表拆成多个独立配置。这里场景总配表也并不是很大可以用总表但是编辑器编辑体验很差所以我想要的是SO编辑器用分立文件导出时收集所有SO文件生成一个json。我们发现永远都是写死、强引用很方便一遇到复杂功能就不行。地图界面两种使用情景现在地图按M打开是显示信息在锚点打开可以点击上面的锚点传送过去。那么最终标记的行为根据模式不同是不同的。还有生成锚点的逻辑不是由维护的场景被显示对象字典而是从那个地图的配置里读取锚点位置。这里MVC又用上了视图是复用的由不同的控制器加载。锚点控制器玩家选地图后去地图配置管理器得到地图图片和锚点位置然后还要换算位置得到换算公式的参考点也在地图位置里那么我们直接给地图配置加一个方法就换算位置。这里就是这个操作用的数据属于哪个类最多就作为哪个类的方法。传送该写传送函数了传入场景名字、锚点序号然后传送过去得机制关卡现在程序还是只维护当前的关卡状态。那么关卡状态管理器改成字典维护多个场景的关卡状态。还要解决传送进入场景、初始进入场景、加载存档进入场景设置玩家位置的不同操作。传送进入有一个锚点索引去地图配置查到位置把玩家放那初始进入要从多个随机位置中选一个加载存档进入放到存档里存的位置。我们为了验证功能先粗暴的把场景切过去然后MyGameMgr会查当前的关卡状态如果不处理还会加载之前场景敌人、NPC、道具那些全部和地形错位。所以切场景前必须切换关卡状态数据。但是这个数据从哪来现在游戏还是分立关卡的。那么现在玩家一进入游戏所有场景都有一个关卡状态只是除了玩家呆的场景其他场景是一个默认关卡状态看起来是个空地图。然后现在的关卡状态文件我们可以叠加加载原本那个场景是空地图现在我们触发一个任务我们写一个叠加函数对里面的对象list、dictionary叠加。我们发现传送时玩家的血量、装备、背包物品都是不变的只有所处的场景、位置变了那么我们直接从起点场景状态数据的玩家状态拷贝一份这里不能是赋值导致同时修改两个场景的玩家状态。需要深拷贝。懒得写深拷贝就json序列化然后复原回来。任系统务的修改从任务Behavior中心到任务管理器中心现在传送到目标场景不报错了但是目标场景是空地图之前开启的任务也没有了。现在任务是MonoBehavior直接放在任务目的地。那么任务需要改成非MonoBehavior它本质是个数据类目的地位置是它一个字段且现在目的地如果不在同一个场景也不应该显示目的地标记。我看了看移动到目的地的任务还需要触发器如果任务不是MonoBehavior那还得再弄一个MonoBehavior放在目的地。然后这个类还得写场景序列化、还原。我们需要的是传送到另一个场景任务数据还在但是任务MonoBehavior不在此场景。那么我们进入场景后开启任务不是靠这个场景里的任务MonoBehavior状态是不是open而是状态文件记录的。任务管理器记录开启的任务也不是记录任务MonoBehavior不在当前场景的任务就不存在MonoBehavior而是就记录任务Id进度。现在的开启任务进入场景后读取玩家状态里已开启的任务任务MonoBehavior不是由恢复关卡状态时创建而是知道玩家开启的任务后如果这个任务的场景和当前场景相同则创建任务MonoBehavior。之前MissionBehavior的设计是只要在这个场景不管没开的、开启的、完成的任务都在场景里会开启的任务和场景锁死。流程是读取关卡状态创建任务BehaviorBehavior在Start发现自己的状态是Open调用任务管理器Open自己。修改后的流程任务管理器从玩家状态存档得到任务列表拿到任务配置对比配置里的场景名和当前场景名一致则让任务配置管理器创建任务Behavior。没开启的任务即使在这个场景也不生成Behavior。马上想到这样设计导致一个问题就是我们是用事件发布监听开启下一个任务如果下一个任务不存在那么没有监听者就无法触发。那么还是把事件发布监听功能都转移到任务管理器。那么需要修改 复原关卡状态不要创建任务Behavior让任务管理器检查场景一致后创建。任务Behavior在Start不要开启自己统一让管理器开启。就是说我们之前以任务Behavior为中心的任务系统本质就和多场景自由传送游戏不兼容应该以数据为中心而Behavior就是任务在自己场景的目的地标记触发器挂点。我们之前设计成任务Behavior为中心因为这个游戏里每个任务都有目的地MonoBehavior方便标记且移动到目的地任务必须有MonoBehavior挂载触发器。如果大部分任务是诸如砍5棵树、捡3个苹果这种没有目的地的我们一开始就不会设计成Behavior中心。任务系统事件发布监听的修改无参事件动态拼接事件名VS字符串参数事件固定事件名现在成功开启了第一个任务和NPC对话后发布的事件无人响应。要让任务管理器响应此事件。要通过事件让任务管理器知道完成哪个任务需要从事件信息到任务Id建立映射关系要么直接通过事件名和任务Id建立关系要么发布string参数的事件事件名固定通过参数判断。因为NPC对话发布事件我只设计了无参事件所以任务管理器这边拿前缀和已开启任务的Id拼接出一些事件名来监听。我们写由Id构造事件名的函数string BuildOpenEventName(string missionId) { return missionId _Open; } string BuildCompleteEventName(string missionId) { return missionId _OK; }添加监听是在任务进入开启列表时移除监听是在任务离开开启列表时。然后又发现问题添加的是完成这个任务的函数但是完成任务是个有参函数需要lambda表达式封装才能成无参函数直接用lambda表达式封装就丢失引用无法移除了。那么再把它引用起来弄一个字典Dictionarystring,Action把lambda表达式放进去移除的时候移除这个任务Id下的值。然后发现NPC对话发布的事件名要重新填了必须包含对应的任务Id且符合格式。而不能就和任务Behavior监听的事件名一致就行。我们发现我们可以手动填保证它符合完成任务的事件名格式也可以就填任务Id由同一个函数组装出事件名问题是这样这个发布事件就变成完成任务专用的了要想让NPC对话触发其他效果如移动物品还要再写发布事件我不想这样决定让这个字符串就直接发布以便可以触发各种效果。那我们就手动符合完成任务的事件名格式。我们越来越发现之前以任务Behavior为中心让它监听发布事件而不是管理器为中心是一种非主流的设计。总结一下这一块的难点就是多个各种发布者发布事件但是要任务管理器一个对象响应并且执行不同方法这些方法本质是一样的但是参数不一样。不过我发现如果发布的是string参数事件任务管理器只要监听一次就够了在这个函数里对参数判断开启列表里有没有这个任务Id。比上面发布无参事件从事件名拆解出参数还要添加好几个监听也要移除好几个要简单。再总结一下无参事件动态拼接事件名接收方要添加N个监听且把同一个有参函数N种参数封装成N个无参函数用一个字典维护添加、移除N次监听。字符串参数事件固定事件名接收方准备一个字符串参数函数添加移除一次监听函数内判断。肉眼可见字符串参数事件方案简单的多。而中途讨论的函数内部构建事件名只是无参事件动态拼接事件名方案下的一种选择手填检查的工作能少一点牺牲了此发布事件的泛用性不太值得使用。然后又遇到问题现在开启任务的函数输入的是一份数据里面包括任务进度因为要支持玩家完成一半的任务加载存档时恢复进度。而开启任务现在传一个字符串就是任务Id。通过事件开启的任务一定是新任务进度为0所以再写一个接收string的方法里面new一份数据。玩家状态数据的迁移之前做存档功能在存档里存玩家的位置那时候是不考虑跨场景。现在把玩家状态数据所在场景名、位置、朝向、HP、装备开启的任务提到一个场景无关文件。然后呢现在游戏还有分立关卡的残余开启任务是关卡状态文件里是open的任务。其实原来的关卡初始任务不需要了我们不会再以进入场景为条件触发一个任务支持场景自由传送后任务都是一个接一个的。现在我们用新的任务系统能跑完一个关卡了但是我们的目标是没有关卡结束的概念全部由任务串联起来。即使玩家做完了任务也可以添加配置文件下次进入游戏添加任务。未来我们还可能把主菜单场景取消。那么接下来我们就要把之前结束关卡的任务改成其他场景的任务了。