《守望先锋》架构设计与⽹网络同步
Overwatch Gameplay Architecture and
Netcode
Timothy Ford
Lead Gameplay Engineer
Blizzard Entertainment
翻译:kevinan
在GDC2017【Overwatch Gameplay Architecture andNetcode 】的分
享会上,来⾃自暴暴雪的Tim Ford介绍了了《守望先锋》游戏架构和⽹网络同
步的设计。⼀一起来看看吧。
哈喽,⼤大家好,这次的分享是关于《守望先锋》(译注:下⽂文统⼀一简
称为Overwatch)游戏架构设计和⽹网络部分。⽼老老规矩,⼿手机调成静⾳音;
离开时记得填写调查问卷;换下半藏,赶紧推⻋车!(众笑)
我是Tim Ford,是暴暴雪公司Overwatch开发团队⽼老老⼤大。⾃自从2013年年夏
季项⽬目启动以来就在这个团队了了。在那之前,我在《Titan》项⽬目组,
不不过这次分享跟Titan没有半⽑毛钱关系。(众笑)
这次分享的⼀一些技术,是⽤用来降低不不停增⻓长的代码库的复杂度(译
注,代码复杂度的概念需要读者⾃自⾏行行查阅)。为了了达到这个⽬目的我们
遵循了了⼀一套严谨的架构。最后会通过讨论⽹网络同步(netcode)这个本
质很复杂的问题,来说明具体如何管理理复杂性。
Overwatch是⼀一个近未来世界观的在线团队英雄射击游戏,它的
主要是特点是英雄的多样性, 每个英雄都有⾃自⼰己的独⻔门绝技。
Overwatch使⽤用了了⼀一个叫做“实体组件系统”的架构,接下来我会简称
它为ECS。
ECS不不同于⼀一些现成引擎中很流⾏行行的那种组件模型,⽽而且与90年年
代后期到21世纪早期的经典Actor模式区别更更⼤大。我们团队对这些架构
都有多年年的经验,所以我们选择⽤用ECS有点是“这⼭山望着那⼭山⾼高”的意
味。不不过我们事先制作了了⼀一个原型,所以这个决定并不不是⼀一时冲动。
开发了了3年年多以后,我们才发现,原来ECS架构可以管理理快速增⻓长的代
码复杂性。虽然我很乐意分享ECS的优点,但是要知道,我今天所讲
的⼀一切其实都是事后诸葛亮 。
ECS架构概述
ECS架构看起来就是这样⼦子的。先有个World,它是系统(译
注,这⾥里里的系统指的是ECS中的S,不不是⼀一般意义上的系统,为了了⽅方便便
阅读,下⽂文统称System)和实体(Entity)的集合。⽽而实体就是⼀一个ID,
这个ID对应了了组件(Component)的集合。组件⽤用来存储游戏状态并且
没有任何的⾏行行为(Behavior)。System有⾏行行为但是没有状态。
这听起来可能挺让⼈人惊讶的,因为组件没有函数⽽而System没有任何字
段。
ECS引擎⽤用到的System和组件
图的左⼿手边是以轮询顺序排列列的System列列表,右边是不不同实体拥有的
组件。在左边选择不不同的System以后,就像弹钢琴⼀一样,所有对应的
组件会在右边⾼高亮显示,我们管这叫组件元组(译注,元组tuple,从
后⽂文来看,主要作⽤用就是可以调⽤用Sibling函数来获取同⼀一个元组内的
组件,有点虚拟分组的意思)。
System遍历检查所有元组,并在其状态(State)上执⾏行行⼀一些操作(也
就是⾏行行为Behavior)。记住组件不不包含任何函数,它的状态都是裸存
储的。
绝⼤大多数的重要System都关注了了不不⽌止⼀一个组件,如你所⻅见,这⾥里里的
Transform组件就被很多System⽤用到。
来⾃自原型引擎⾥里里的⼀一个System轮询(tick)的例例⼦子
这个是物理理System的轮询函数,⾮非常直截了了当,就是⼀一个内部物理理引
擎的定时更更新。物理理引擎可能是Box2d或者是Domino(暴暴雪⾃自有物理理
引擎)。执⾏行行完物理理世界的模拟以后,就遍历元组集合。⽤用
DynamicPhysicsComponent组件⾥里里保存的proxy来取到底层的物理理表
示,并把它复制给Transform组件和Contact组件(译注:碰撞组件,
后⽂文会⼤大量量⽤用到)。
System不不知道实体到底是什什么,它只关⼼心组件集合的⼩小切⽚片(slice,译
注:可以理理解为特定⼦子集合),然后在这个切⽚片上执⾏行行⼀一组⾏行行为。有些
实体有多达30个组件,⽽而有些只有2、3个,System不不关⼼心数量量,它只
关⼼心执⾏行行操作⾏行行为的组件的⼦子集。
像这个原型引擎⾥里里的例例⼦子,(指着上图7中)这个是玩家⻆角⾊色实体,可
以做出很多很酷的⾏行行为,右边这些是玩家能够发射的⼦子弹实体。
每个System在运⾏行行时,不不知道也不不关⼼心这些实体是什什么,它们只是在
实体相关组件的⼦子集上执⾏行行操作⽽而已。
Overwatch⾥里里的(ECS架构的)实现,就是这样⼦子的。
EntityAdmin是个World,存储了了⼀一个所有System的集合,和⼀一个所有
实体的哈希表。表键是实体的ID。ID是个32位⽆无符号整形数,⽤用来在
实体管理理器器(Entity Array)上唯⼀一标识这个实体。另⼀一⽅方⾯面,每个实
体也都存了了这个实体ID和资源句句柄(resource handle),后者是个可
选字段,指向了了实体对应的Asset资源(译注:这需要依赖暴暴雪的另⼀一
套专⻔门的Asset管理理系统),资源定义了了实体。
组件Component是个基类,有⼏几百个⼦子类。每个⼦子类组件都含有在
System上执⾏行行Behavior时所需的成员变量量。在这⾥里里多态唯⼀一的⽤用处就
是重载Create和析构(Destructor)之类的⽣生命周期管理理函数。⽽而其他
能被继承组件类实例例直接使⽤用的,就只有⼀一些⽤用来⽅方便便地访问内部状
态的helper函数了了。但这些helper函数不不是⾏行行为(译注:这⾥里里强调是为
了了遵循前⾯面提到的原则:组件没有⾏行行为),只是简单的访问器器。
EntityAdmin的结尾部分会调⽤用所有System的Update。每个System都
会做⼀一些⼯工作。上图9就是我们的使⽤用⽅方式,我们没有在固定的元组组
件集合上执⾏行行操作,⽽而是选择了了⼀一些基础组件来遍历,然后再由相应
的⾏行行为去调⽤用其他兄弟组件。所以你可以看到这⾥里里的操作只针对那些
含有Derp和Herp组件的实体的元组执⾏行行。
Overwatch客户端的System和组件列列表
这⾥里里有⼤大概46不不同的System和103个组件。这⼀一⻚页的炫酷动画是⽤用来
吸引你们看的(众笑)。