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用于二维精灵的显示。

相关推荐
techdashen15 小时前
性能比拼: Rust vs C++
java·c++·rust
无名之逆15 小时前
[特殊字符] Hyperlane:Rust 高性能 Web 框架的终极选择 [特殊字符]
服务器·开发语言·前端·网络·后端·http·rust
非衣居士1 天前
Lua程序设计笔记
lua·游戏开发
Source.Liu1 天前
【学Rust写CAD】14线性插值函数(加入color.rs)
rust·cad
Yeauty2 天前
Rust 与 FFmpeg 实现视频水印添加:技术解析与应用实践
rust·ffmpeg·音视频
爆炸头林冲2 天前
Rust 学习笔记(一)
笔记·学习·rust
aimmon2 天前
Rust从入门到精通之入门篇:3.Hello World
开发语言·后端·rust
爆炸头林冲2 天前
Rust安装并配置配置vscode编译器
开发语言·后端·rust
救救孩子把2 天前
uv:Rust 驱动的 Python 包管理新时代
python·rust·uv
aimmon2 天前
Rust从入门到精通之进阶篇:15.异步编程
开发语言·后端·rust