前言
ECS 系统数据的保存方式分为两种:
一种是像数据库一样,数据表结构作为 Entity 每个字段作为 Component,这种结构迭代数据比较快(要不人家数据库也不会这样设计了)。但是如果想修改 Entity 结构就很困难。就像不能随便动数据库表结构一样。
还有一种是将相同的 Component 数据保存在一个数组中。然后拿 Entity 的 Id 作为索引。这种方式迭代就比较低效,因为迭代每次要访问的地方都是随机的。但是如果想动 Entity 的结构就比较容易。因为都是按列存的,对已经存在的数据没影响。
作为游戏引擎来讲,我们把 Entity 定义好很少会在运行时动态修改它,所以一般都会采用第一种方案。
在 Bevy 的 ECS V2 中提供了两种选项,默认是我们之前聊的第一种。可以手动修改成第二种。
1
2
3
|
world.register_component(
ComponentDescriptor:🆕:<MyComponent>(StorageType::SparseSet)
).unwrap();
|
Component/Entity/System
可以理解成数据库的字段就是 Component,一个数据库条目就是一个 Entity。只不过 Component 是一种功能,而并不是单纯的数据类型。
Entity 是一些 Component 的集合,提供统一的 EntityId
System 具体执行逻辑的地方
Resource
全局共享的数据,例如游戏状态等等
Stage
Stage 的概念是和 System 的运行息息相关的。它直接影响 System 的执行顺序。
库中预定义的 Stage 都在 bevy_app/src.lib.rs 的 CoreStage 中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
pub enum CoreStage {
/// Runs once at the beginning of the app.
Startup,
/// Name of app stage that runs before all other app stages
First,
/// Name of app stage that runs before EVENT
PreEvent,
/// Name of app stage that updates events. Runs before UPDATE
Event,
/// Name of app stage responsible for performing setup before an update. Runs before UPDATE.
PreUpdate,
/// Name of app stage responsible for doing most app logic. Systems should be registered here by default.
Update,
/// Name of app stage responsible for processing the results of UPDATE. Runs after UPDATE.
PostUpdate,
/// Name of app stage that runs after all other app stages
Last,
}
/// The names of the default App startup stages
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
pub enum StartupStage {
/// Name of app stage that runs once before the startup stage
PreStartup,
/// Name of app stage that runs once when an app starts up
Startup,
/// Name of app stage that runs once after the startup stage
PostStartup,
}
|
除了预定义的这些 Stage 我们还可以基于此定义自己的 Stage:
1
2
3
4
5
6
7
8
9
10
|
.add_stage_before(
CoreStage::Update,
MyStage::BeforeRound,
SystemStage::parallel(),
)
.add_stage_after(
CoreStage::Update,
MyStage::AfterRound,
SystemStage::parallel(),
)
|
自由度应该算是拉满了吧。
我们默认使用的 add_system
其实就是默认把system 插在 CoreStage::Update 处。
而 add_startup_system
其实就是把 system 默认插入在 StartupStage::Startup 处。
add_statge 系列函数可以指定 stage 的执行模式 SystemStage::parallel
。
如果就是单纯的想区分两个 System 的顺序,不想在全局再添加过多的 stage 可以再 system() 后调用 label 和 after before 等函数进行指定。
Resource 的是添加
insert_resource
可以将我们构造好的对象直接添加进引擎
init_resource::<T>()
会在 FromWorld 中调用类型的 Default trait 的 default() 方法。其实默认行为就是直接利用 Default 初始化默认值。
System 中对数据的修改:
如果想 World 实例中做例如生成新玩家的操作需要用到 Commands,它是一个缓存队列,类似 DirectX 的命令队列,它只保证该操作在未来会执行,但不是立即执行。
如果想对 World 实例做立即生效的修改需要用 thread_local 的 system 直接拿到 world:&mut World。
System 本地变量
System 可以有用 Local<State>
包裹的本地变量,这样,对于相同类型的System 的不同实例也可以有自己独立的状态。
还可以通过 config 手动配置:
1
2
3
4
|
fn foo(value: Local<usize>) {
}
app.add_system(foo.system().config(|c| c.0 = Some(10)));
|