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

相关推荐
龚子亦13 小时前
【Unity开发】数据存储——XML
xml·unity·游戏引擎·数据存储·游戏开发
该用户已不存在18 小时前
Zig想要取代Go和Rust,它有资格吗
前端·后端·rust
侑虎科技1 天前
Unity中利用遗传算法训练MLP
性能优化·游戏开发
用户1774125612441 天前
不懂装懂的AI,折了程序员的阳寿
rust
量子位2 天前
vivo自研蓝河操作系统内核开源!Rust开发新机遇来了
rust·ai编程
祈澈菇凉2 天前
rust嵌入式开发零基础入门教程(六)
stm32·单片机·rust
祈澈菇凉2 天前
rust嵌入式开发零基础入门教程(二)
开发语言·后端·rust
祈澈菇凉2 天前
rust嵌入式开发零基础入门教程(一)
rust
祈澈菇凉2 天前
rust嵌入式开发零基础入门教程(三)
开发语言·单片机·rust
Source.Liu2 天前
【unitrix】 6.13 类型级整数的按位取反(Not)操作实现(not.rs)
rust