文章目录
 

Blog

DevNotes_21Spring

  |   DevNotes   |   No comment

这是2021年一个没有做完的游戏项目的复盘

2021年3月到6月中旬,我们在学校里接着课程作业组建了一个团队,试图想做一个规模不算小的游戏。1月2月做了不少底层设计,3月中旬全员开工。但是到了6月上旬的时候,仅仅是架子最基础的部分完成了,少数能玩的内容也是bug满天飞,数值非常不讲究。不过虽然如此,我依然认为这个设计来说还是挺有趣的,如果所有内容都做出来了会好玩的,但我们并不打算接着做下去了。对于没有完成预先设定的目标,我觉得这不是核心成员能力的问题。首先,时间真的太短了,要做的内容真的太多了,这是一个有RPG要素的塔防并且还是roguelike的游戏流程,我们只有7个人,并不具备足够的专业素养,也不是所有人都足够上心;其次,团队上的问题,虽然我们组织和规划上确实出了些问题,但里面确实一大部分来源于学校这个环境而无法改变的,最终导致迭代次数和时间都增加了,工程上也出现了大量擦屁股行为,再加上本身前期规划得就有点过于紧凑而没有冗余了,导致后期没有时间也没有精力与动力继续做下去了。

 

接下来细节讲讲每个地方都出了什么问题,随便写写,没有怎么组织和排版。

 

 

 

组合大于继承。

X用三个多月的时间亲自表演了一番 继承小于组合。

c#的空引用压根就不占内存,配置的时候是可序列化的,所以会实例化出来,但事实上完全可以再加一个 IfContain或者IfHas之类的,然后在加载的时候赋空,就完全不占内存了。而且就算真的占,区区配置文件能占几个内存,资源走addressable加载,配置里面就留个assetReference,其他那些都是些数和枚举和string这样的东西,最后省下来的内存可能还没有monoScript本身的内存大。那这个时候组合就好多了,判空也行,is判类型也行,总之,内部配置文件里面的几个数据类型别搞复用,真没意义,而且到后面做具体解释的时候抓数据也抓不到。

这刚好也就是做架构,做成解释为重还是配置为重的考量。要我说肯定是解释为重的,因为必定会改会增加会减少东西,前期设计只会给出指导方向。这不是设计的问题,是本身就是这样的,就是只会给一个方向一样的东西,无论是GD的控制参数还是RLD的LD Pattern,是不会、不能、也不需要给到非常具体的内部细节实现的。这样也刚好符合设计模式原则的对扩展开放对修改关闭。那么如果配置做的很重,然后又把配置本身的数据结构做的很不直观,内部数据复用,或者内部还有额外的数据初始化与构造逻辑,那这真不行,因为属实没法扩展,而且修改起来又很难,真的屎山,再加上X坚决不写注释和偏执级的超短命名,最后就是别说扩展了,哪怕直接上去改也改不动。最后就是让本就不太合理的架构更是雪上加霜,patch摞patch,hack叠hack。结果到后面还转头赖我一开始不说好后面要做啥。啊这。我自己做的那些元素的架构是极易扩展的,甚至还有几个元素真的就是只有架子和一两个测试用例,需要加东西的时候可以直接在代码里很容易地加,而且都是解释型的代码,写起来很快,更改也可以做到完全不影响已有内容。而且,这真不是设计的问题。这里很容易会觉得是“啊那不就是你做游戏设计的时候就没考虑好要做什么结果没法做架构之后又要改架构吗”,如果你会这么觉得那我觉得你的游戏设计水平实在是需要提升——我们的游戏设计内容做得很全面,所有体系很自洽,控制参数设计得全面而合理,并且都给到了相应的可以发挥的游戏元素,LD Pattern也有不少,而且事实上TD游戏的LD Pattern仅仅是把市面上一下子就能想到的那些都做一遍,内容也是极多的,所以这不是游戏设计的时候没做好,是我们都做好了,只是没有给到最终那个最具体的表现,比如这个技能到底打多少伤害打多远有什么附加效果,这个真没法给,但与之相关的元素是确确实实都设计出来了,也考虑到了,甚至还记录在requirement上面了,比如技能可以带位移,技能可以造成这样那样的buff,技能可以造成位移,技能可以造成不同的来源不同比例的伤害。但X就是属于那种,做到了一些,但没有完全做到requirement里的,没做出来,但又搞出来了那么一点。毕竟强配置型的架构在它编译能够通过的那一刻,它能做到的事情与不能做到的事情就已经定死了(除非这个配置体系完整到还能做代码生成),上限和下限是同一个线,结果就是整个系统又没有办法改,扩又扩不动,又不写注释,后果就是只能在外面逻辑层狂加hack实现。就比如一个,冰冻效果会停止位移。好家伙,首先造成伤害的时候没有做能上buff的,让加上,费了半天劲加上了,结果要给这个冰冻区分了吧,用enum区分当然合理,但原本的buff的数据结构里面不包含enum,那只能改原有的配置体系,给里面加一个enum的位置,然后顺带着整个内部数据复用和初始化相关的逻辑就都要改,这一改还顺便会让之前已经配好的文件都废掉而需要再重新做一遍配置,改完了冰冻加上去了,但是冰冻还要停止位移啊,但是原本的位移并没有考虑到会被数据项影响也没有留回调点,结果在位移逻辑的前面又加了个单独的Check函数,内容就是Check现在整个数据模型内部是否包含一个冰冻的tag。好家伙结果后面我要再加一个束缚呢,还得再来一个单独的Check是否有束缚tag的函数。结果就是,普通的位移前面加了一大堆各种各样的Check,而且还没有注释,函数名又特别短,越搞越像一团浆糊。而且这个check各种tag是完全在DataModel内部进行的,事实上停止位移的时候动画也应当停,结果按X写的原有逻辑,动画也没有拿到负责表现(View)的层级上去,而是在角色Behaviour(Controller)上处理的,结果效果就是位移倒是停了但是动画没停而且甚至还没办法停因为Behaviour抓不到,而且如果分离到表现层上去就更抓不到了——灾难发生了却还因为小灾难阻塞了大灾难的发生,绝了。

当然这个做成配置为重还是解释为重的,不是说有一种就一定好于另一种。我认为这个应该是要同时考虑项目的情况、开发周期的情况和团队人员配置与能力的情况来考量的。我也看到了很多那种基于行为树啊可视化工具啊那些,做得非常详细非常周到的那种,配置为重的做法,但我们项目里不需要这么做:一个原因是 做具体实现的,不是那种纯策划或者配表工去做的,而是我或者其他能够写代码也了解项目架构的人去做的,那这个时候就不很需要做把配置过程做得那么完善与复杂了,不如做什么就直接插代码进去用代码实现,这样扩展和修改都很直接也很可控。

绝了。一大早说了数据和行为分离,DataModel只管数据和数据直接相关的逻辑,好家伙DataModel里面上来就是rigidbody字段给我在数据模型里面做位移。位移怎么样也是控制器上的事情吧这怎么就进数据模型了。

MVC框架,如果做一做扩展,那就可以是MVCSE,即Model-View-Controller-Service-EventBus。那对应到游戏架构里面,那差不多就是 DataModel数据模型-ArtHelper艺术表现-Behaviour行为与控制层-Service/Manager管理器-EventBus事件总线。其中MVC三个部分不宜互相耦合,差不多是Behaviour contain DataModel和ArtHelper,DataModel和ArtHelper之间尽量只是只读的关系,如果要有业务交流也尽量走时间总线。Service/Manager就是各种各样的管理类和中间类,什么地图上的元素的管理、伤害判定的管理、刷怪管理、玩家行为记录管理之类的,最顶层的Manager就是GameManager,唯一单例。然后事件总线,事件都从总线走,而且是能走总线都尽量走总线。

这个架构不是说一定就得这么做,也不是说这么做一定就好,这是根据项目的大小、开发周期、开发人员和团队成员的能力综合而来的,我从19年的项目开始这么做,经过不断的实践与改进与调整,觉得这种开发思想对于小团队的规模不算大的项目来说,是比较合适的。架构之外的思路也是比较合适的,比如这个总线,除了事件可以走总线,很多其他的东西也可以用同样的思路来处理,比如配置文件和存档,比如音效和特效。这个项目里面,X做了一些特效,但是不知道是TA爱好者都是有某种神秘而共通的执念还是怎么了,我们的特效明明不需要做得很复杂,就那么明着的几种,但非要做一套程序化生成的特效出来,然后还要挂专属的脚本来刷新。一个就算了,结果几乎是一大半特效都这么搞,结果就是做得又慢,每个特效的播放、跟踪、停止的方法全都不一样,而且专属脚本里也没有注释,字段和方法名又超短。到了真的要开始加特效的时候问题一下就浮夸起来了,至少一半的特效没法进总线,因为播放它们需要专属脚本上的专属方法传很多专属参数,然后我的总线里面又不仅仅是播放,还有预加载、跟踪、停止之类的方法呢,这也用不成,因为那些特效没法预加载,而是只能直接放场景里,因为挂的专属脚本上面有很多序列化引用然后它们自己还作为了很多别的东西的序列化引用,跟踪也没法跟踪,因为程序化生成之后还会有一些hack实现,比如特殊的旋转角度和缩放方式,最后停也没法停。麻了,全麻。

=

团队上的问题,我们自己有一些问题,在学校这个环境下做事本身也会有一些问题。环境的问题没法消除,但我们一开始没有把这些问题当成需要需要严肃看待和处理的问题,结果就是越到后期,人的问题越来越多,再加上没法消除掉的环境自身的问题存在,等到出现影响团队和项目的状况的时候,基本上已经是来不及了。学校的问题就是,大家都是同学,平起平坐,拉来的人只能是普通的同学的合作关系,完全到不了“共事”的程度,一个是你没有办法为大家的工作确立standard,因为大家是完全平等的,你还要考虑别人啊人家来帮你做事情还想能够自由发挥呢还有自己的想法呢,standard一确立那别人就会想啊我帮你做个事情怎么还这么多要求还只能按照你的想法来做不让我做自己想做的了。所以standard不能有。另一个是后面的review阶段也不能有,但在这种环境下不review的结果就是几乎所有非主导者亲手做的东西基本上都不能如意,一个原因是确实大家经验都不足,很难比较容易地做到一个足够好的程度,另一个原因就是没有standard,结果就是如果要review那基本上绝大多数时间 都是reject,但是打回重做的次数这么多概率这么高的时候,就不再是工作打回了而是人之间打起来了,所以就不能有review过程。看,恶性循环出现了,大家经验不足,派发任务的时候又不能有standard,然后就要花费更大的精力派发任务,甚至都不能算是派发了,而是“做思想工作”,然后又不能总是去定工期也不能总是去催,然后收任务又要花精力和时间,又因为没有standard也不是派发的任务,所以结果绝大多数时候都不能尽如人意,然后还要自己去改。这么折腾下来,花费的时间和精力都远比一开始就自己做还要多了。

结果我们从3月下旬开始做,4月派发了很多任务,4月下旬收上来了一些,但是很多东西都不太行,然后5月上旬再迭代,然后恶性循环不断循环,到5月下旬的时候大家心态基本都开始炸了,东西不行,没有standard导致大部分资产质量都不太行,迭代重做的话又会觉得很不甘,毕竟做了一个月的东西现在又要重做,但又不得不改因为效果确实不好;派发任务完全就是在浪费所有人的时间,因为发任务收任务再修改再扯皮要花很多时间和精力,还会花很多心态。

经验就是,在学校这种环境下,很难做出来什么有一点规模的游戏,一个是时间每次都很短,也就三个多月,期间还有其他的课程和学校自身的各种事情;另一个是这种合作模式就不行,这哪里是合作,简直就是耗所有人的心态和时间。但是我们的同学们并不会遇到这样的问题。因为他们做的东西太少了项目规模太小了。就好比做试卷,我们知道满分100,但以我们目前的能力离满分很远,但尽可能地要去看更多的题、每个题都试试,最后可能就得了四五十分,甚至不及格;但是我们的同学们只会去做试卷的最前面也是最简单的两三道题,满分十分他们就做八分九分,完成度有个百分之八九十,非常喜人非常光鲜,不像我们费了半天劲焦头烂额还只拿了百分之四五十。但是总量不一样啊!你看看那些做的东西,滥用模板和插件,代码瞎jb胡抄乱扒,凡是有dialogue内容的几乎必用fungus插件(是的没错连个dialogue这么基础的内容都自己写不出来),凡是有3c的也全是插件的内容,因为自己完全做不出来。就这些东西,你找来个中专生我给他培训半个月人家自己再做一个月都能做出来,非常光鲜亮丽,但实际上玩法又浅又窄,设计也十分粗糙,但架不住体量实在是太小了,这种体量下的游戏项目根本不需要什么团队合作、也不需要规划与设计,因为真的太小太小了,全靠个人能力就能完全cover住。

当然人家又不是真的想做独立游戏的,甚至大多数人都不想在这行待着,还不是找个厂混个执行啊运营岗混着就行了,道不同也不好说什么。

后来想到一个更恰当的例子,就好比大家在学做饭,有人根本就不喜欢做饭,而剩下在自己做饭的同学里面,绝大多数都只会泡方便面,是的他们连煮方便面都做不到,因为既然从大一到大四根本就没有什么像样的要求,教学团队也没有人是从业者,更没有人懂如何做游戏,所以只是以“能吃”为唯一评判标准。那么既然泡方便面就“能吃”了,为什么还要学着做别的东西呢?我们很激进地、甚至有些冒险地去尝试了如此之多的东西,热菜凉菜汤和点心全都要试着做一下,结果在大三结束的时候只有两三道菜能吃,比起泡方便面甚至还吃不饱。只是我们不愿意一直泡方便面罢了。

No Comments

Post A Comment