泛型系统

Bevy 系统只是普通的 Rust 函数,这意味着这些函数是可以泛型化的,对不同的 Rust 类型或值进行参数化处理,相同的系统可以多次复用。

组件类型泛型化

你可以使用泛型类型参数来指定你的系统可以对哪些组件类型(以及哪些实体)进行操作。

这在与 Bevy 状态结合使用时会很有用。你可以根据状态对不同的实体集做同样的事情。

例子:清理

一个直观的用例是用于清理。我们可以做一个通用的清理系统,用于将所有具有某种组件类型的实体销毁掉,然后,在退出不同的状态时运行它。

use bevy::ecs::component::Component;

fn cleanup_system<T: Component>(
    mut commands: Commands,
    q: Query<Entity, With<T>>,
) {
    for e in q.iter() {
        commands.entity(e).despawn_recursive();
    }
}

菜单实体可以用 cleanup::MenuExit 来标记,游戏地图的实体可以用 cleanup::LevelUnload 来标记。

我们可以将通用的清理系统添加到我们的状态转换中,来处理各自的实体:

/// Marker components to group entities for cleanup
mod cleanup {
    use bevy::prelude::*;
    #[derive(Component)]
    pub struct LevelUnload;
    #[derive(Component)]
    pub struct MenuClose;
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum AppState {
    MainMenu,
    InGame,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_state(AppState::MainMenu)
        // add the cleanup systems
        .add_system_set(SystemSet::on_exit(AppState::MainMenu)
            .with_system(cleanup_system::<cleanup::MenuClose>))
        .add_system_set(SystemSet::on_exit(AppState::InGame)
            .with_system(cleanup_system::<cleanup::LevelUnload>))
        .run();
}

使用 Trait

当你需要为每种类型提供某种不同的方法或功能时,你可以把它和 Trait 结合起来使用。

例子:Bevy 的摄像机投影

(这是 Bevy 本身的一个用例)

Bevy 有一个 CameraProjection trait。不同的投影类型,如 PerspectiveProjectionOrthographicProjection 都实现了这个 trait, 为如何响应调整窗口大小、计算投影矩阵等提供正确的逻辑。

有一个通用的系统 fn camera_system::<T: CameraProjection + Component>,它用于处理所有具有给定投影类型的摄像机,它将在适当的时候调用 trait 方法(比如在窗口调整事件中)。

Bevy 手册中的自定义相机投影实例展示了这个 API 的作用。

使用常量泛型

既然 Rust 支持 Const Generics,那么函数也可以通过值来参数化,而不仅仅是类型。

fn process_layer<const LAYER_ID: usize>(
    // system params
) {
    // do something for this `LAYER_ID`
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_system(process_layer::<1>)
        .add_system(process_layer::<2>)
        .add_system(process_layer::<3>)
        .run();
}

需要注意的是,这些值在编译时是静态/常量。这可能会是一个严重的限制。在某些情况下,当你或在犹疑你可以使用常量泛型时,你可能最终意识到你实际上想要的是一个运行时值。

如果你需要通过传递一些数据来"配置"你的系统,你可以使用一个 ResourceLocal

注意:从 Rust 1.59 开始,对使用枚举值作为常量泛型的支持还不稳定。要使用枚举,你需要 Rust Nightly 版,并启用实验中和不稳定的特性(把这个放在你的 main.rslib.rs 的顶部):

#![feature(adt_const_params)]