Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

GDC守望先锋

概述

聊到ECS方式的游戏设计,就想到了Timothy Ford在GDC 2017提到的。在参考里面我也贴了b站大佬翻译的视频,和 GDC Vault里面的英文原视频;以及云风的blog里面的分析,大概描述了他的理解。需要理解可能需要看几遍,毕竟别人开发这块东西都花了很多的时间。

规则

Components没有functions
Systems没有状态
将共享的代码放入Utils
将复杂的副作用队列放入组件中进行延时处理,特指单例
Systems之间不允许调用;

death spiral类似雪崩的糟糕局面。

需要想一个办法拆分复杂问题,把问题聚焦到一个较小的集合,提高每个子任务的内聚性。

云峰的理解

ECS优势在于什么

传统的很多游戏引擎是基于面向对象来设计的,游戏中的东西都是对象,每个对象有一个叫做 Update 的方法,框架遍历所有的对象,依次调用其 Update 方法。有些引擎甚至定义了多种 Update 方法,在同一帧的不同时机去调用。

这么做其实是有极大的缺陷的,我相信很多做过游戏开发的程序都会有这种体会。因为游戏对象其实是由很多部分聚合而成,引擎的功能模块很多,不同的模块关注的部分往往互不相关。比如渲染模块并不关心网络连接、游戏业务处理不关心玩家的名字、用的什么模型。从自然意义上说,把游戏对象的属性聚合在一起成为一个对象是很自然的事情,对于这个对象的生命期管理也是最合理的方式。但对于不同的业务模块来说,针对聚合在一起的对象做处理,把处理方法绑定在对象身上就不那么自然了。这会导致模块的内聚性很差、模块间也会出现不必要的耦合。

总的来说、需要想一个办法拆分复杂问题,把问题聚焦到一个较小的集合,提高每个子任务的内聚性。

Component 没有方法,而 System 则没有状态,只是对定义好的 Component 状态的加工过程。而许多 System 中很可能会处理同一类问题,涉及的 Component 类型是相同的。如果这个有共性的问题只涉及一个 Entity ,那么直观的方法是设计一个 System ,迭代,逐个把结果计算出来,存为 Component 的状态,别的 System 可以在后续把这个结果作为一个状态读出来就可以了。

提问阶段

有人问他是否考虑使用double buffer的方案,Timothy并不反对这个方案,他觉得蛮好的,只是没有集成。

1:01:07 讨论了数字的精度问题,他们应该是精确到了1米*1024。问了物理引擎相关的东西。基于相同输入,所以应该能获取相同的输出。他们将浮点数离散化到毫米精度的时候,还是会有些担心里面会出一些什么问题。其实处理的情况可以接受,可以去看一下Phil Orwig的讲座。其实浮点型的数字计算,的确和IEEE标准有关。

里面也使用了动态的navmesh,他们是通过固定帧来保证动态的的阻挡的一致性。

参考

[1] Timothy Ford演讲-翻译版
[2] GDC-Vault-Raw
[3] 云风的分析文档
[4] Networking Scripted Weapons and Abilities in Overwatch
[5] Quake-III-Arena