从上一篇Bevy Component我们基本认识了ECS中的Component,这篇文章,我们将介绍Entity。
Entity和Component是紧密相连的,从比较高的视角看,Entity可以拥有零个或者多个Component实例。
每个Entity只能拥有一个Component类型的一个组件,也就是同类组件只能有一个。可以在整个实体的生命周期中动态添加或删除这些类型。
Entity实际上是代表其相关组件索引的标识符。
Entity即索引
Entity类型是一种轻巧的标识符,仅用于生成他的World(Bevy中的概念,可以暂时理解为应用)。
// https://github.com/bevyengine/bevy/blob/main/crates/bevy_ecs/src/entity/mod.rs
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct Entity {
generation: u32,
index: u32,
}
Entity本身只有index和generation两个属性,该类的设计可以保证Entity在维持内存连续性的基础上完成快速的数据插入删除,这正是Bevy高效率的原因之一。
Entity在World的视角
每个World都会有一个Entity的列表,其中包含3组实体ID:
freelist:已经吧被释放的ID。reserved:曾经在freelist中,但是现在被保留着。pengding:暂时不存在的新的ID计数。
Entity中的generation和index确保同时存在的两个实体将永远不会共享相同的索引。
每当具有给定索引的实体被销毁时,其对应的“代次”(Generation)会递增。这一机制用于记录该索引被重复使用的次数。
这些唯一的标识符使Bevy能够以惰性方式分配它们。先保留ID,然后分配。
当然我们在使用Bevy开发游戏的时候,不需要关心这些,生成一个Entity,只需要:
fn spawn_health(
mut commands: Commands
) {
commands.spawn(Health(0));
}
关于索引的分配流程,如下图所示:
graph TD
A[Entity创建] --> B[分配新索引]
B -->|首次分配| C[索引N, 世代0]
C --> D[销毁实体]
D --> E[加入空闲列表]
E --> F[再次分配]
F --> G[索引N, 世代+1]
总结
到此,关于Bevy中ECS的基本介绍就完成了,后续,我们将会研究Bevy的其他方面。