[gdc17]《守望先锋》的EntityComponent架构

gdc17上面由暴雪的Tim Ford带来。

由于原文是gdcvault上面的付费内容,贴下腾讯的gad上的翻译:http://gad.qq.com/article/detail/7212152?bsh_bid=1732845294

本文可以说是对于entity/component系统(entity component system简称ECS了)有了一个更深的理解和探索,非常喜欢的一篇文章,而且和卡马克的一篇讲“functional programming”的非常好的呼应。

本文可以说是一个“学后感”,结合tim ford的讲解,加上自己的解读和实践中的看法,也有独特的价值吧。


==传统的Entity-Component System==
细节可以看wikipedia:https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system 
98年thief这个游戏算是一个开始将Entity-component system发扬光大的游戏,也有近20年的历史了。 
大家且不要以为这是一个游戏开发的标配,实际开发中还真遇到过就是不用这种结构的,依靠着继承以及写出超大规模类的方式硬写出来,额滴神,那代码叫一个多一个乱,那bug叫一个多,好不容易调好了,直接设为禁区(谁也不敢碰了),但是随着游戏新的需求还得改,被给与相关任务的程序员颇有发配边关的感觉。。。 

传统的ECS,可以想象是一个车的结构,entity就是这个车,component就是车的组件,我们根据需要,按个雨刷的component,车就能清理挡风的雨水了,按个音响系统,就能听音乐了等等。 
每个component有自己的数据和功能,然后这样组装的方式,构成一整个车。 
实际使用中,结合architype和data driven,实用性非常的好:

  • architype(也就是unity引擎中的prefab),把已有的带着预设好的component的entity,保存成template,游戏中需要直接生成一个instance非常的方便
  • 根据配置或者实际中需要,随时随地的增加component,我们的entity就开始有各种我们需要的功能 
背后就是一个composite设计模式,把具体功能分散在component的实现中,分治&解耦和!!


==《OverWatch》的ECS==


这个是一个对overwatch的ecs的小结。

这里和传统的最大不同就是,传统的是component除了数据之外,自己有大量的函数,也可以修改自己,这些函数就是component的功能所在。

先贴一个overwatch的ecs的示意图:


(整体示意图)


(爬墙的system,对应到几个component)


(attach系统,操作这么几个component)

所以我们可以看到overwatch则把component的函数去掉了(注意这里不是外放到system中,不是一个概念),然后是通过system来完成各个功能。

tim ford对此的解释,是各个系统看component的时候,概念上更加清晰,他举得例子是一个树:


这个树不同人看是不同的东西,鸟看就是可以栖息的地方,主人看是景观,木工看是用来造家具的木材。

所以一个树如何定义,要看使用它的system(就是鸟,主人和木工),所以把函数拿出到system中来,而不是放到component中,就更符合由使用者来定义,而不是树自己定义自己的概念。

可以说是对于实际情况的一个更加透彻的认识。

这种认识带来更好的解耦和,进一步降低了系统的复杂度。


==个人的认识==

笔者看来,tim ford的解释是一个方面,笔者还想加一个:这个本质上是object-oriented programming到functional programming的转变。

细节可以看“重温“卡马克谈functional programming in c++”文章。

functional programming一个厉害的地方就是把state的变化从object oriented programming的封装倾向变成一个显示呈现的方式。

这更加容易让程序员拥有更好的对于系统state变化的控制力,进一步也控制了系统的复杂度。


tim ford也反复提到了复杂度这个问题,最好的情况就是system只读component的state,而不做修改。

但是实际情况是修改的情况必须要发生,这种时候会引入大量的复杂度,实现的程序员要做好控制。


==实际中的变通==

//singleton component

开始overwatch程序团队是按照比较理想的方式构建系统:system没有状态,component没有函数。

但是时间长了,system没有状态这个有点不切实际,比如玩家输入状态,放在system就太tmd方便了,于是就干了。

结果在开发走到死亡重放的时候就跪了,当有两套系统,各子系统有自己的状态的时候,就各种问题。

最后一个折衷是有了一个singleton component存放全局状态。

可以说是对于原有系统的扩充。

//util function

一些system之间共享的操作,就进一步提取成util function,多个系统之间使用。

不过不本来就应该这么做么。。。

//延迟执行

像放特效这种,tim ford表示这个很多时候要跨系统做判断等等,可以使用延迟执行的方法来降低复杂度。

就是各个system中放特效的操作都放到一起去执行,彼此之间还可以排序,做数量控制等等。


==和卡马克的functional programming in c++的呼应==

我们可以看到,卡马克在他的文章中也是强调复杂度的控制,状态改变的控制,以及实际情况中的折衷优化。

和tim ford整个说法非常好的呼应,读的过程中也一下子想起卡马克的文章了,所以在写这个之前,也写的重温篇。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值