Bevy ECS Systems与普通函数的关联机制
Bevy游戏引擎的ECS架构通过SystemParamFunction机制将普通函数转换为可执行系统。开发者编写的参数为SystemParam的函数,会被宏自动实现SystemParamFunction trait,然后通过IntoSystem转换为FunctionSystem。执行时,系统获取参数并调用原始函数,同时正确处理生命周期以保证内存安全。这种设计使开发者能专注于业务逻辑,Bevy自动处
在 Bevy 游戏引擎中,ECS(Entity-Component-System)架构是其核心。开发者可以通过编写简单的函数来创建系统(Systems),这些函数能够自动转换为 Bevy 可执行的系统。
1. 基础概念
1.1 系统参数(SystemParam)
在 Bevy 中,系统参数是实现了 SystemParam trait 的类型。常见的系统参数包括:
Commands:用于执行实体操作命令
Res<T> 和 ResMut<T>:用于访问资源
Query<T>:用于查询实体和组件
EventReader<T> 和 EventWriter<T>:用于读写事件
Local<T>:用于存储系统本地状态
fn setup_camera_and_environment(
mut commands: Commands, // Commands 实现了 SystemParam
mut meshes: ResMut<Assets<Mesh>>, // ResMut<T> 实现了 SystemParam
mut materials: ResMut<Assets<StandardMaterial>>, // ResMut<T> 实现了 SystemParam
) {
// 函数体
}
1.2 SystemParam trait
SystemParam trait 是连接普通函数和 Bevy 系统的核心。它定义了系统参数如何初始化、验证和获取:
unsafe trait SystemParam: Sized {
type State: Send + Sync + 'static;
type Item<'world, 'state>: SystemParam<State = Self::State>;
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State;
unsafe fn get_param<'world, 'state>(
state: &'state mut Self::State,
system_meta: &SystemMeta,
world: UnsafeWorldCell<'world>,
change_tick: Tick,
) -> Self::Item<'world, 'state>;
}
2. 系统参数函数(SystemParamFunction)
2.1 SystemParamFunction trait
为了使函数能够成为 Bevy 系统,需要实现 SystemParamFunction trait:
pub trait SystemParamFunction<Marker>: Send + Sync + 'static {
type In: SystemInput;
type Out;
type Param: SystemParam;
fn run(
&mut self,
input: <Self::In as SystemInput>::Inner<'_>,
param_value: SystemParamItem<Self::Param>,
) -> Self::Out;
}
这个 trait 定义了:
In:系统输入类型
Out:系统输出类型
Param:系统使用的参数类型
run:执行函数的方法
2.2 宏自动生成实现
Bevy 使用宏来自动生成 SystemParamFunction 的实现。在 function_system.rs 中定义了 impl_system_function! 宏:
macro_rules! impl_system_function {
($($param: ident),*) => {
// 为没有输入参数的函数实现 SystemParamFunction
impl<Out, Func, $($param: SystemParam),*> SystemParamFunction<fn($($param,)*) -> Out> for Func
where
Func: Send + Sync + 'static,
for <'a> &'a mut Func:
FnMut($($param),*) -> Out +
FnMut($(SystemParamItem<$param>),*) -> Out,
Out: 'static
{
type In = ();
type Out = Out;
type Param = ($($param,)*);
#[inline]
fn run(&mut self, _input: (), param_value: SystemParamItem< ($($param,)*)>) -> Out {
fn call_inner<Out, $($param,)*>(
mut f: impl FnMut($($param,)*)->Out,
$($param: $param,)*
)->Out{
f($($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, $($param),*)
}
}
// 为有输入参数的函数实现 SystemParamFunction
impl<In, Out, Func, $($param: SystemParam),*> SystemParamFunction<(HasSystemInput, fn(In, $($param,)*) -> Out)> for Func
where
Func: Send + Sync + 'static,
for <'a> &'a mut Func:
FnMut(In, $($param),*) -> Out +
FnMut(In::Param<'_>, $(SystemParamItem<$param>),*) -> Out,
In: SystemInput + 'static,
Out: 'static
{
type In = In;
type Out = Out;
type Param = ($($param,)*);
#[inline]
fn run(&mut self, input: In::Inner<'_>, param_value: SystemParamItem< ($($param,)*)>) -> Out {
fn call_inner<In: SystemInput, Out, $($param,)*>(
_: PhantomData<In>,
mut f: impl FnMut(In::Param<'_>, $($param,)*)->Out,
input: In::Inner<'_>,
$($param: $param,)*
)->Out{
f(In::wrap(input), $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(PhantomData::<In>, self, input, $($param),*)
}
}
};
}
通过 all_tuples!(impl_system_function, 0, 16, F); 这一行,宏会为参数数量从 0 到 16 的所有函数生成实现。
3. IntoSystem trait
3.1 IntoSystem trait 定义
IntoSystem trait 负责将实现了 SystemParamFunction 的函数转换为实际的系统:
pub trait IntoSystem<In: SystemInput, Out, Marker>: Sized {
type System: System<In = In, Out = Out>;
fn into_system(this: Self) -> Self::System;
}
3.2 IntoSystem 实现
对于实现了 SystemParamFunction 的函数,Bevy 提供了 IntoSystem trait 的实现:
impl<Marker, F> IntoSystem<F::In, F::Out, (IsFunctionSystem, Marker)> for F
where
Marker: 'static,
F: SystemParamFunction<Marker>,
{
type System = FunctionSystem<Marker, F>;
fn into_system(func: Self) -> Self::System {
FunctionSystem {
func,
state: None,
system_meta: SystemMeta::new::<F>(),
archetype_generation: ArchetypeGeneration::initial(),
marker: PhantomData,
}
}
}
4. FunctionSystem
4.1 FunctionSystem 结构
FunctionSystem 是包装普通函数的实际系统类型:
pub struct FunctionSystem<Marker, F>
where
F: SystemParamFunction<Marker>,
{
func: F,
state: Option<FunctionSystemState<F::Param>>,
system_meta: SystemMeta,
archetype_generation: ArchetypeGeneration,
marker: PhantomData<fn() -> Marker>,
}
4.2 系统执行流程
当系统实际执行时,流程如下:
Bevy 调用 FunctionSystem::run_unsafe
该方法内部调用 F::Param::get_param 获取实际参数
然后调用 self.func.run(input, params),即调用宏生成的 run 方法
宏生成的 run 方法解构参数元组并调用原始函数
unsafe fn run_unsafe(
&mut self,
input: SystemIn<'_, Self>,
world: UnsafeWorldCell,
) -> Self::Out {
let change_tick = world.increment_change_tick();
let param_state = &mut self.state.as_mut().expect(Self::ERROR_UNINITIALIZED).param;
let params = unsafe { F::Param::get_param(param_state, &self.system_meta, world, change_tick) };
let out = self.func.run(input, params);
self.system_meta.last_run = change_tick;
out
}
5. 完整转换流程
从普通函数到可执行系统的完整流程可以概括为:
函数定义:开发者编写一个普通函数,其参数都实现了 SystemParam trait
SystemParamFunction 实现:通过宏自动生成 SystemParamFunction trait 实现
IntoSystem 实现:Bevy 为实现了 SystemParamFunction 的函数提供 IntoSystem 实现
FunctionSystem 创建:通过 IntoSystem::into_system 将函数转换为 FunctionSystem
系统执行:当系统运行时,Bevy 调用 FunctionSystem 的 run_unsafe 方法,最终调用原始函数
// 1. 函数定义
fn setup_camera_and_environment(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// ...
}
// 2-4. 转换为系统
let system = IntoSystem::into_system(setup_camera_and_environment);
// 5. 系统执行时,Bevy 内部会调用类似这样的代码:
// system.run_unsafe((), world);
6. 生命周期管理
每个系统参数都需要正确处理生命周期:
'w 生命周期关联到 World 中的数据
's 生命周期关联到系统参数的状态
通过宏生成的实现,这些生命周期被正确地传递和处理,确保了内存安全。
更多推荐

所有评论(0)