本周主要完成两个底层系统:首先是CalendarSystem,将系统时间转换为符合游戏历法的日期-时间结构,并提供给其他系统使用。其次是IntervalExecuterSystemBase,也就是应用前者的基础上,制作的“定时执行系统”的基类。
说到Calendar可以聊一下我现在策划里的游戏历法设定,需要说明的是这只是早期设计,也许以后会根据实际体验修改。(大概devlog里的所有策划聊天都适用于这句说明_(:з」∠)_)
首先,在我的游戏里的时间运行速度是现实的120倍,也就是说12分钟为现实一天。也有正常的日出日落,在夜间角色们也是要睡觉的。目前计划玩家操作的角色也需要睡觉,当玩家角色睡觉期间,游戏会以4倍速快速运行。
然后按照参考的古代时间制度资料,每天拆分为12个时辰“子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥”,每时辰也就等于我们现在的2小时,每时辰拆分为8个刻。刻就是游戏文本描述中的最小时间单位了,比如有名的“午时三刻”。
接着,作为模拟经营类游戏,一般不会真的把一年设定为365天,太拖节奏。因此我这个游戏里设定一年为48天。每季度12天。不设月份,但是设置节气,每个季度下3个节气,因此总共是从中国二十四节气中跳着选择了十二个节气。
节气表
在每个节气日会具有一定的特殊景观表现,之后对所有天气类型的参数进行调整,例如在“清明”当天会下一天的雨,而立秋那天,树叶会变黄。
节气影响表
表中可以看到跟随节气的变化,大量气候参数也会发生变化,气候参数将主要影响到作物成长。可以来我的策划维基看具体描述:
历法设置:https://taohuayuanwiki.a2hosted.com/doku.php?id=%E6%9C%BA%E5%88%B6:%E6%97%B6%E9%97%B4
气候变动:https://taohuayuanwiki.a2hosted.com/doku.php?id=%E6%9C%BA%E5%88%B6:%E6%B0%94%E5%80%99
第二个主要底层系统是IntervalExecuteSystemBase,Entitas框架自带的ExecuteSystem默认放在Update里每帧运行,对于比如角色移动来说是Ok的,但是对于我这款游戏里的一些特殊需求来说,每帧运行是不必要而且很浪费机能的,例如土壤含水量的计算每刻进行一次即可,植物成长度的计算每天进行一次即可。
因此根据这种需求,额外包装ExecuteSystem利用前述的CalendarSystem提供的日期时间制作了IntervalExecuteSystemBase,提供定时执行的功能。
这个系统本身是很简单没有什么可说的,就相当于内置一个闹钟,隔一段时间执行一次子系统的Execute方法,但是这里有一个可能的问题:首先,使用定时系统的功能大多是“所有地块运算含水量”“所有地块运算温度”这种大规模遍历运算,本身就有可能产生卡顿。另一方面定时系统的设计有可能会导致多个系统在某些时刻会同时运算,比如“每刻系统A""每小时系统B""每天系统C",在0:00的时候会全部被触发执行,导致每天午夜一定会瞬间卡一下。
因此我额外给IntervalExecuteSystemBase中增加了一个排队和拆分机制,排队是指当有一个定时子系统在某一帧运算时,其他定时在同一时刻的子系统向后排队到下一帧。而拆分机制是指子类可以在代码里把自己的运算自由拆分成多个部分,每帧执行一部分,当执行完毕后通知出来,下一帧才轮到下一个排队的子系统。这样的设计的代价就是实际运行时间不会完全等同于预定的闹钟时间,会多少延后几帧,因此系统会向OnExecute方法传入实际运行时间,代码要基于这个时间进行计算,即可解决。
事实上UnityECS也有对这个问题的考虑和解决办法,他提供了一个允许任意分段的for循环,以及允许设定循环结束后的后置计算,并且for循环本身支持多核并行,比我这个高级多了,将来还是需要转移到UnityECS去啊。
最后看一下我在干净环境下的性能测试,测试系统以1/4秒为间隔,每次运行时进行100次打印输出:
未分拆优化
可以看到尖峰相当明显,在进行打印输出时的单帧耗时在50ms左右,FPS掉到了30以下。
然后是对循环进行了1/4拆分之后:
分拆优化后
分拆优化之后,之前单帧的打印输出工作分担到了连续的四帧上。单帧耗时降到了不到16ms,FPS升到了约70。
测试中是简单的遍历输出打印,实际应用中可以是以玩家角色为中心进行扩散状遍历执行,然后以块数分拆。 这种分拆机制原理非常简单,无非就是for循环的分段,然而之所以能够进行如此简单的分拆,全仰赖ECS框架的革命性的思考方式的转变。
暂无关于此日志的评论。