撰写了文章 发布于 2019-10-28 15:39:15
《斧头人(Axe Man)》十月开发日志
这局德扑,是我亚瑟·摩根赢了。谁赞成,谁反对?
游戏简介
《斧头人(Axe Man)》(Github 页面,英文博客)是一款正在开发的单人回合制 Unity Roguelike 游戏。上个月 以来实现了大部分基本功能,发布了第一个试玩版 0.0.1。
图 1:演示动画,十月。
图 2:演示动画,八月。
十月份没有太大进展。月初通关了泰坦佛和大表哥,月末开始玩辐射三:天外世界(我的第三个人物,Jack Cooper,离开翡翠谷仅仅用了十七个小时)。这次谈两个话题,时间系统和读取 XML 文件系统,翻译自以下两篇文章:
图 3:鬼灭左轮:黑水镇的亚瑟·摩根。
时间系统
如果把时间系统看作一个数据容器,它应当具备四个功能:
- 把游戏对象添加进容器,并按照指定顺序排列它们。
- 从容器中移除指定对象。
- 获取“指针”当前指向的对象。
- 让“指针”指向下一个对象。
此外,还有两个与数据容器密切相关的操作:
- 让其它游戏组件移动“指针”。
- 当“指针”移动时,提醒其它游戏组件。
那么首先,我们来看一下数据容器。Schedule 实现了 `ISchedule`,使用 `private List<GameObject> schedule` 存储游戏对象。
图 4:ISchedule。
实现 `ISchedule.GotoNext()` 和 `ISchedule.Remove()` 的时候,避免引用空对象。
图 5:Schedule。
我们使用 TurnManager 移动数据容器“指针”。`TurnManager` 实现了接口 `ITurnManager`。
图 6:ITurnManager。
`StartTurn()` 和 `EndTurn()` 将发布多个事件通知其它游戏组件:某个游戏对象的回合开始或结束了,`NextActor()` 封装了多个公开方法:
图 7:NextActor()。
`TurnManager` 还订阅了事件 `CheckingSchedule`,隐式地调用 `NextActor()`。
图 8:CheckingSchedule。
游戏开始的时候,我们手动调用一次 `TurnManager.StartTurn()`。接下来,只要每个游戏对象在合适时机发布 `CheckingSchedule` 事件,时间系统就能自动运转起来了。
读取 XML 文件
我先前写过存储和读取游戏数据的 文章,这次做了少许改进,让同一套代码适用于不同结构的 XML 文件。如果我们把读取系统看作一个黑盒子,它需要接受文件路径和数据标签,输出特定类型的数据。我们不妨把这套系统拆解成两个组件:
- XML 文件 --> [读取文件] --> XElement
- XElement,数据标签 --> [读取特定的 XElement] --> 游戏数据
第一个组件 SaveLoadXML 封装了 `XElement.Load()`。
图 9:SaveLoadXML。
读取 `XElement` 的组件比较有趣。理想情况下,我们可以设计这样一个接口:
图 10:IGameData。
但是实际游戏中有两个问题。第一,数据标签的类型不止一个。第二,XML 文件的结构同样不止一种。
图 11:XML 文件。
为此,我们针对每种 XML 文件设计专门的接口。这些接口拥有类似的名字,比如 `ISettingData` 和 `IActorData`。它们的公用方法名字相同,比如 `GetIntData()` 和 `GetStringData()`,但是输入的参数不同。
图 12:读取 XElement 的接口。
ActorData 和 SettingData 实现了上述两个接口。它们都有一个类似这样的私有方法:`bool TryGetData(DataTag dataTag, out XElement xElement)`。以 `SettingData` 为例,具体代码如下。
图 13:SettingData。
从外部看,我们调用 `SettingData.GetBoolData()` 或者 `ActorData.GetStringData()`的时候,只要输入特定的数据标签,就能得到需要的数据,不用考虑数据来自哪一份文件。
目录