refs: https://zhuanlan.zhihu.com/p/30538626
1 什么是ECS架构
ECS是 Entity-Component-System(实体-组件-系统)的缩写,其模式遵循组合优于继承的原则。
- Entity 游戏内的每个基本单元都是一个实体,每个实体里面有多个组件。
- Component 每个组件仅包含代表其特征的数据,例如:移动相关的组件仅仅包含速度、位置、朝向等属性。一旦一个实体拥有了MoveComponent
组件便可以认为它拥有了移动的能力。
- System 系统是用来处理拥有一个或者相同的几个组件的实体集合的工具,其只拥有行为(在系统中没有任何的数据)。在移动相关的组件的例子中,处理移动的系统仅仅关心拥有移动能力的实体,它会遍历所有拥有MoveComponent
组件的实体,并且根据相关的数据(速度、位置、朝向等),更新实体中的组件的属性。
实体与组件之间是一对多的关系,实体拥有怎么样的能力,完全取决于其拥有哪些组件,通过动态的删除与修改实体中的组件,可以改变实体的行为。
2 ECS的基本结构
一个使用ECS架构开发的游戏的基本结构如下:
world是实体与系统的集合,而实体就是一个ID,这个ID对应了组件的集合。组件用来存储游戏状态并且没有任何行为,系统拥有处理实体的行为但是没有状态。
3 详解实体、组件与系统
3.1 实体
实体是一个概念上的定义,是游戏世界中的一个独特的物体,是一系列组件的集合,为了方便区分不同的实体,在代码层面上一般用一个ID来进行表示。所有组成这个实体的组件将会被这个ID标记,从而明确哪些组件属于该实体。由于其是一系列组件的集合,因此可以在游戏运行的时候在实体中添加一个或者删除一个组件。
样例:
- Player(Position, Sprite, Velocity, Health)
- Enemy(Position, Sprite, Velocity, Health, AI)
- Tree(Position, Sprite)
注:括号前为实体名,括号内为该实体拥有的组件
3.2 组件
一个组件是一堆数据的集合,可以使用C语言中的结构体来进行实现。它没有方法,即不存在任何的行为,只用来存储状态。
样例:
- PositionComponent(x, y)
- VelocityComponent(X, y)
- HealthComponent(value)
- PlayerComponent()
- EnemyComponent()
注:括号前为组件名,括号内为该组件拥有的数据
3.3 系统
系统是游戏中用来处理逻辑的部分,一个系统就是对拥有一个或多个相同组件的实体集合进行操作的工具,它只有行为,没有状态,即不应该存放任何数据。
举个例子,游戏中玩家要操作对应的角色进行移动,由上面两部分可知,角色是一个实体,其拥有位置和速度组件,那么怎么根据实体拥有的速度去刷新其位置呢,MoveSystem
(移动系统)登场,它可以得到所有拥有位置和速度组件的实体集合,遍历这个集合,根据每一个实体拥有的速度值和物理引擎去计算该实体应该所处的位置,并刷新该实体位置组件的值,至此,完成了玩家操控的角色移动了。
系统这里比较麻烦,还存在一个常见问题:由于代码逻辑分布于各个系统中,各个系统之间为了解耦又不能互相访问,那么如果有多个系统希望运行同样的逻辑,该如何解决,总不能把代码复制 N 份,放到各个系统之中。UtilityFunction(实用函数) 便是用来解决这一问题的,它将被多个系统调用的方法单独提取出来,放到统一的地方,各个系统通过 UtilityFunction 调用想执行的方法,同系统一样, UtilityFunction 中不能存放状态,它应该是拥有各个方法的纯净集合。
样例:
- MoveSystem(Position, Velocity)
- RenderSystem(Position, Sprite)
注:括号前为系统名,括号内为该系统关心的组件集合