撰写了文章 发布于 2017-05-01 16:53:13
Devlog 9
最近的主要进展都是在程序结构方面的,没有多少可见的进度展示,对于非程序员来说,可能会有点枯燥哈。
那么,在上次讨论过Job之后,随着玩家的建设指令,就能够创建出来一大批Job了。于是接下来就要有这些Job的执行者,也就是角色(Character)了。
Character
先用“人话”描述下University里的角色吧,在University里,预定将会有搬运工、建筑工、教职员工等工作人员、当然还有最重要的学生和教师。玩家不能直接操作任何角色,而是通过建设规划、政策调整等方式间接影响他们,并且为他们提供尽可能优良的生活环境。
那么,沿着这个“人话”的描述去思考类结构的话,最直接想到的就是**关系,也就是Character父类,以及一批针对各个“职业”的子类,比如StudentCharacter, TeacherCharacter。不过,这样直接的继承结构(Is-A),也许并不是最优选择。
而我认为更好的结构是组合结构(Has-A):也就是说Character类始终只有一个,但他拥有个Components容器,然后基于不同职业会用到的“功能”去设计各种Component,从而赋予Character各种能力,比如一个身为搬运工的Character应当拥有TranportComponent,一个身为建筑工的Character应当拥有BuildComponent,等等。
Is-A VS Has-A
Is-A还是Has-A?,或者说继承还是组合?这方面网上有很多讨论可以看,不细说其中的技术细节了。我就说说放在University这个具体项目下的区别。
选择哪种结构,其目的都是要解决特定目的:如何在代码中表达不同职业的人的区别?如果使用继承的方式,那么由此带来的角色的身份固化,会导致至少有3个问题解决起来很别扭。
1. 功能重叠时:我们知道如果多个子类都有同一个行为时,就将行为上移,由父类实现,比如现在所有角色都要有睡觉功能,那么睡觉就放到根类去就好了。但是如果只有部分角色有重叠功能呢,比如学生和老师需要吃饭,而其他工人不需要。(黑心老板啊)那么,继承的解决方案就是再增加个中层,来区分“员工”与“师生”。然后给师生父类实现。但是如果多次遇到这个需求时,势必导致继承结构的复杂化。而组合结构,只需要在运行时为不同身份的角色Add或者不Add进“睡觉组件”就可以了。
2. 运行时改变时:如果一个学生在毕业时选择留校当教师,也就是身份变动了,怎么处理?继承结构的话可能要新建一个教师对象,然后拷贝数据过去。但组合结构的话,只需要移除学生相关的组件,添加教师相关组件就可以了。
3.运行时扩展时:我们假设经过玩家的科技树升级后,建筑工不仅能建设,也能搬运物资了,这里该怎么办?继承结构的话,最蠢的方案就是拷贝粘贴,增加一个新子类“BuilderWithTransporterCharacter”…或者聪明一点把建设和搬运的行为独立出去,然后让各种角色调用,这样来防止拷贝粘贴,但是这样其实就是自发走往"Has-A"了。而组合结构,完全可以在运行时的任何时候增减组件。
所以,结论其实就是一句话:优先考虑组合而非继承。
而这也正是OOP的扩展设计原则之一“Composition Over Inheritance”,参考:
https://en.wikipedia.org/wiki/Composition_over_inheritance
NoobPotato 1年前
太昊 [作者] 1年前
关于后半段,我在写完之后也在继续思考的时候意识到其实Character的概念可以继续上移为Sprite,从而包含进设施,因为角色与设施相比除了能移动并没有本质区别,所以就让“移动”成为一个Component组件就好了;
再然后其实可以再近一步上移把地图拼块也包含进去,这时候类名叫什么呢,已经包括了我游戏里所有Model了,所以可以叫GameObject了。
于是突然意识到其实Unity就是这么做的,也正是你所说的包含有状态管理和属性管理的总父类GameObject。在管理了所有公共属性的同时,可以拽上去任何Component组件。
所以我从一开始就让自己的Model层抄Unity结构就好了嘛!摔!
发布
Shitake 1年前
发布
龙骑士无面者 1年前
太昊 [作者] 1年前
发布
Coollen MMX 1年前
想多了, 自己搞着玩的东西别搞的太技术向
发布