@猴与花果山童鞋已经阐述了ECS的主要概念。此文主要从技术和工程角度简单探讨游戏行业中设计模式的演变历史和ECS的意义。
设计模式产生的动机,往往源于尝试解决一些存在的问题。游戏开发领域技术架构的宏观目标,大体包括以下目标:
现代 Entity Component System 的概念,以及对游戏开发领域的意义:
ECS 并非《守望先锋》所独有和原创,事实上近年来以 ECS 为基础架构逐渐成为国际游戏开发领域的主流趋势。
采用 ECS 的范式进行开发,思路上跟传统的开发模式有较大的差别:
ECS 框架,至少有以下优点:
若要了解为何会出现 ECS 这样的模式,以及它所试图解决的问题,需要考虑一下历史进程:
注重于实现相关算法、功能和逻辑,代码只要能实现功能就行,怎么直观怎么来。比如
class Player { int hp; Model* model; void move(); void attack(); };
类似这样完全没有或很少架构设计的代码,在项目规模增大后,很快变得臃肿、难以扩展和维护。
设计模式是语言表达能力不足的产物。 —— 某程序员 那么,作为他山之石,GoF 基于 Java 提出的设计模式,能否有效解决游戏开发领域的问题?
大家还记得当年国内风靡一时的游戏引擎 OGRE 么?
OGRE中用到的设计模式 - 逍遥剑客 - 博客频道 - CSDN.NET
OGRE 总有那么些学院派的味道,试图通过设计模式的广泛使用,来提高代码的可维护性和可扩展性。
然而,个人对游戏开发领域大规模使用 OOP 设计模式的看法:
那么,有没有办法简化和沉淀出游戏开发领域较通用的模式?
Unity3D 是个使用了 Entity Component 模式的成功的商业引擎。
相信使用过 Unity3D 的童鞋,都知道 Unity3D 的 Entity Component 模式是怎么回事。(在Unity3D 中,Entity 叫 GameObject)。
其优点:
看起来,Unity3D 已经在很大程度上解决了游戏设计领域通用模式的问题。然而,其 Entity Component 模式仍然存在一些问题:Component 仍然延续了一些 OOP 的思路。比如:
那么,综合以上所说的各种问题,一个基于 C++ 的现代 Entity Component System,应该是什么样子?
具体案例,可以参考 [EntityX](https://github.com/alecthomas/entityx),一个开源的 C++ ECS 框架。
一一实现了前述现代 ECS 的各种概念:Entity 只是个 ID,Component 存储数据,System 实现关联多个 Component 的行为。
代码味道:
struct Position { Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} float x, y; }; struct Direction { Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} float x, y; }; struct MovementSystem : public System<MovementSystem> { void update(entityx::EntityManager &es, entityx::EventManager &events, TimeDelta dt) override { es.each<Position, Direction>([dt](Entity entity, Position &position, Direction &direction) { position.x += direction.x * dt; position.y += direction.y * dt; }); }; };
如上,实现了两类 Component:Position 和 Direction。
MovementSystem 只关心同时具有两类 Component 的 Entity。
一些值得说的特点:
除了可以提高缓存命中率外,新世代的 ECS 还可以通过分析数据依赖和读写关系,来实现 System 间的并行。比如更新时, System A 需要读 组件1,System B 需要读 组件1、写组件2,System C 需要写 组件1,那么调度时可以把 System A 和 System B 分配到不同线程处理,之后再处理 System C。原贴中也一笔带过提到了这方面的优化。然而对于复杂的 C++ 游戏来说,这个目标在实践上的可行性具有比较大的障碍:难以确保团队中的熊孩子不小心写出非线程安全的代码。
不过,Rust 给这个问题带来了解决方案。可以参考 Rust 实现的并行 ECS 框架:slide-rs/specs
Rust 的语言特性在编译期保证了线程安全,只需声明一下 System 对 Component 的访问权限如:
type SystemData = (ReadStorage<'a, Velocity>, WriteStorage<'a, Position>); 这样,即可安全地获得多线程带来的性能提升。
本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度,google,bing,sogou 等
© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有