没想到这么快就要重构了,原来常对说思路不清,这下轮到自己头上了。
问题产生在项目不停在膨胀,在思路不够明晰的情况下,每多写一行代码,可控性就少一分。就如同不停的盖房子,在没有蓝图的情况下,盖一个一两层也是没有问题的,但是随着高度不断攀升,随时都有崩塌的危险。这可能是每一个没有规划好的项目都会碰到的问题,而这个问题越晚爆发越糟糕。现在需要重新梳理思路,把前面的代码重构。

基本的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+-------------+
| |
| load config |
+-------------+
| |
+----v-----+ +-----v-----+
| new game | | load game |
new runtime data | or level | | or level | load runtime data
+----------+ +-----------+
| |
+--v---v-----+ +------------+
| play game +----------> ??? |
change runtime data | | | 不可预知 |
+------------+ +------------+
|
+-----v------+
| exit |
save runtime data | save |
+------------+

左边的部分是一个一般软件的流程,但在游戏中,play是不可预知的,可能停电,可能关机等等,实时保存运行时数据变得必要。这个导致我前面没有考虑到了最大的坑。
这里的实时保存,并不是针对所有的数据,这里根据保存的密集度,我分成三类:

  1. 创建保存,比如地牢,一旦生成,在新建之前都将保持数据。
  2. 触发保存,比如道具,一旦拾取,道具当前状态为删除。
  3. 实时保存,比如英雄,每一步都需要储存,玩家肯定不愿意重走一遍。特别的某些随机的的部分,再来一遍数据就不同了,这也是roguelike的魅力,每一局都是独特的。

吐槽一下面向对象

世界既不是对象构成,也不是函数构成,世界怎么构成取决观察者的看法。所以无论面向对象,还是函数式,我的看法是什么合适用什么。但是对于面向对象有一个非常糟糕的事就是:

  1. 不易变化
  2. 容易臃肿

不易变化:比如,游戏中需要加入英雄,那么创建一个hero类,过一会,又需要加入狼,那么创建一个wolf类,过一会,英雄需要变成狼,这下麻烦了,建立一个creatures的父类,hero,wolf都继承,各种行为做成接口,看起来似乎解决了。突然又有一个英雄变成桌子的需求,这下就开始为各种特殊性写丑陋的代码了,需求不确定是设计游戏的常态,但对于面向对象来实现这简直就是噩梦。

容易臃肿:比如,有一个创建地牢的类,负责创建地牢,刚开始对象不多,这里面的功能也比较单一,比较好维护,随着需求不断扩展,类的功能越来越多,然后剩下的就是要们忍受,要么重构。

理想的状态是数据驱动,面向组合:这是对于一坑提出的解决方案,entitas是一个优秀的ecs工具,针对不确定的变化,可以非常有效的解决,比如:游戏中需要加入英雄,我们可以为英雄建立一个实体,为它添加渲染,行为,属性等组件,需要变成狼,更改渲染对象为狼的模型,更改行为方式为狼的行为方式,属性略作调整即可,变成桌子,更改渲染对象为桌子,删除行为组件,属性换成桌子属性即可。系统针对某一个或某一些组件实现行为,特别需要保持功能单一,对于不同的对象就是组件的组合,由组件引发的附带的行为功能组合。即便如此,还是被我用成了面向对象,把一个个的system写的臃肿,现在就整理思路,开始改变.