在 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 生命周期关联到系统参数的状态
通过宏生成的实现,这些生命周期被正确地传递和处理,确保了内存安全。

Logo

一站式虚拟内容创作平台,激发创意,赋能创作,进入R空间,遇见同道,让优质作品闪耀发光。​

更多推荐