Bevy Plugin

随着项目的发展,为了整个项目更加模块化和可扩展性、健壮性。我们可以把我们得游戏逻辑其拆分为Plugin

就像我们写代码使用的VS CodeIDEA 等开发工具一样,我们可以通过插件扩展工具的功能,而插件之间又不会互相影响,可以随着我们的喜好增加删除插件。Bevy正是按照该思路拓展的。

声明插件

rust 复制代码
// 声明一个插件结构体
struct PlanetPlugin;

// 实现一个插件的trait
impl Plugin for PlanetPlugin {
    fn build(&self, app: &mut App) {
        // logic code...
    }
}

我们需要声明一个结构体PlanetPlugin用来表示插件,然后实现Plugin trait来编写插件相关的逻辑。仔细观察Plugin trait,它需要我们实现一个build接口,该接口我们能够获取到一个App对象,正如我们之前在main函数中做的那样,当我们有了App之后,我们就可以插入系统、添加新的插件等一些操作。

实现逻辑

在之前的Bevy Sprite文章中,我们学会了如何简单操作一个2D精灵,我们按照这个思路,实现一个简单的Plugin

rust 复制代码
// 定义移动速度
const SPEED: f32 = 800.0;

// 定义星球的结构体
#[derive(Component)]
struct Planet {
    // 星球的名字,和资源名称绑定
    name: String,
    // 尺寸,因为是正方形,所以一个数值即可
    size: u32,
    // 初始化位置
    init_pos: (f32, f32),
    // 定义移动的键码,左上右下的顺序
    moving: (KeyCode, KeyCode, KeyCode, KeyCode),
}

// 星球插件结构体
struct PlanetPlugin;

// 结构体实现
impl Plugin for PlanetPlugin {
    fn build(&self, app: &mut App) {
        // 添加一个观察者
        app.add_observer(ob_planet)
            // 添加一个移动星球的系统
            .add_systems(Update, moving_planet);
    }
}

// 移动星球
fn moving_planet(
    time: Res<Time>,
    keyboard_input: Res<ButtonInput<KeyCode>>,
    mut sprite_query: Query<(&mut Transform, &Sprite, &Planet)>,
) {
    let delta_time = time.delta_secs();

    sprite_query.iter_mut().for_each(|(mut t, s, p)| {

        let mut x = 0f32;
        let mut y = 0f32;

        if keyboard_input.pressed(p.moving.1) {
            y = 1f32;
        } else if keyboard_input.pressed(p.moving.3) {
            y = -1f32;
        }

        if keyboard_input.pressed(p.moving.0) {
            x = -1f32;
        } else if keyboard_input.pressed(p.moving.2) {
            x = 1f32;
        }

        Vec3::new(x, y, 0.0).try_normalize().map(|v| {
            t.translation += v * delta_time * SPEED;
        });
    })
}

// 观察者用于观察Planet的添加,当用户添加Planet的时候,我们就添加新的 Sprite 以及 Transform。
fn ob_planet(
    trigger: Trigger<OnAdd, Planet>,
    query: Query<&Planet>,
    mut commands: Commands,
    asset_server: Res<AssetServer>,
) {
    let entity = trigger.entity();

    let _ = query.get(entity).map(|planet| {
        commands.entity(entity).insert((
            Sprite {
                image: asset_server.load(format!("{}.png", planet.name)),
                custom_size: Some(Vec2::new(planet.size as f32, planet.size as f32)),
                ..default()
            },
            Transform::from_xyz(planet.init_pos.0, planet.init_pos.1, 0f32),
        ));
    });
}

插件的基本逻辑已经实现完成了,我们现在可以添加到App中了。

rust 复制代码
fn add_planet(mut commands: Commands) {
    commands.spawn(Planet {
        name: "alpine".to_string(),
        size: 48u32,
        init_pos: (-100.0, 0.0),
        moving: (KeyCode::KeyA, KeyCode::KeyW, KeyCode::KeyD, KeyCode::KeyS),
    });

    commands.spawn(Planet {
        name: "ocean".to_string(),
        size: 32u32,
        init_pos: (100.0, 0.0),
        moving: (
            KeyCode::ArrowLeft,
            KeyCode::ArrowUp,
            KeyCode::ArrowRight,
            KeyCode::ArrowDown,
        ),
    });
}

fn main() {
    
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_plugins(PlanetPlugin)
        .add_systems(
            Update,
            add_planet.run_if(input_just_released(KeyCode::Digit1)),
        )
        .run();
}

// 添加相机
fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());
}

其实我们早就开始用插件了,不是吗?DefaultPlugins!。

我们将PlanetPlugin插件添加到系统,然后编写一个add_planet系统用来添加Planet

用到的两个星球资源如下:

现在,运行我们的代码:

当我们按下按键1的时候,窗口中会出现两个星球,这两个星球可以通过键盘的上下左右和WASD来分别控制。

如果我们只看main函数的代码,会发现他相当简洁。Bevy 中的Plugin 可以更好的组织代码、梳理我们的游戏逻辑,对于关联性特别强的逻辑,我们推荐使用Plugin 进行组织,如果对外部有依赖,便通过Event来互相通讯。

什么是观察者?

读者可能会注意到,上述代码中add_observer并没有讲过。那么,什么是Observer

Observer是一个监听Trigger的系统。每个触发器都适用于特定的事件类型。

Bevy有一些内置的触发器,我们可以使用它们:

类型 描述
OnAdd 添加实体时触发
OnInsert 插入实体时触发
OnRemove 在实体消失时触发

注意,Observer其实就是一个系统,不过这个系统的用法和其他系统稍微不一样,我们用我们的代码举例:

rust 复制代码
// 定义Observer
fn ob_planet(
    // Observer 需要一个 Trigger
    trigger: Trigger<OnAdd, Planet>,
    query: Query<&Planet>,
    mut commands: Commands,
    asset_server: Res<AssetServer>,
) {}

// 添加 Observer 的方法需要使用 add_observer
app.add_observer(ob_planet)
  1. 使用Trigger作为一个系统的参数。
  2. 通过add_observer添加这个Observer

这样,当我们添加一个新的Planet的时候,便会进入到我们ob_planet Observer,此处,我们添加了Sprite用于二维精灵的显示。

相关推荐
明月看潮生4 小时前
青少年编程与数学 02-019 Rust 编程基础 02课题、开始编程
开发语言·算法·青少年编程·rust·编程与数学
rayylee16 小时前
Ubuntu也开始锈化了?Ubuntu 计划在 25.10 版本开始引入 Rust Coreutils
linux·ubuntu·rust
Source.Liu1 天前
【PhysUnits】2.2 Scalar<T> 标量元组结构体(scalar/mod.rs)
rust
vivo互联网技术1 天前
FunProxy - 使用 Rust 构建跨平台全链路测试抓包代理工具
软件测试·rust·抓包·代理
@PHARAOH1 天前
WHAT - Rust 静态派发(Static Dispatch)和 动态派发(Dynamic Dispatch)
开发语言·后端·rust
Source.Liu1 天前
【PhysUnits】2 Scalar<T> 标量元组结构体(scalar/mod.rs)
rust
muyouking111 天前
Rust 中 Arc 的深度分析:从原理到性能优化实践
开发语言·性能优化·rust
muyouking111 天前
Rust 与 Golang 深度对决:从语法到应用场景的全方位解析
rust
UestcXiye2 天前
Rust 学习笔记:关于结构体的例题
rust
vortex52 天前
Kali Linux 安装 Rust 环境简明教程
linux·运维·rust