掘金 后端 ( ) • 2024-05-24 17:18

前言

bevy是一款基于纯rust的游戏库, 即使你没有任何rust基础, 但只要你有一点编程的基础知识, 便能看懂如何基于bevy开发简单的小球移动游戏.

想了解bevy游戏详情的同学们, 欢迎点击github开源库查看

相较与其他游戏库, bevy诞生的最晚, 2020年才开发出来, 但是它的star数量已经高达32k多, 这足以说明该游戏库已经取得广大码农的认可及支持. 好, 废话不多说, 让我们开始bevy小游戏旅程吧!

准备工作

毫无疑问, 你需要在你电脑上安装rust, 安装详情不在这里多加赘述. 安装好了之后, 执行rustc --version, 控制台输出版本信息即可.

image.png

在本地新建文件夹bevy-learn, 进入该文件夹内, 执行cargo new first-bevy命令之后, 你会发现在bevy-learn中, 新增了first-bevy文件夹, 并且该文件夹内已有如下图的红框文件

image.png

Cargo.toml相当于前端npm的包管理依赖文件

image.png

main.rs程序的主入口函数, 里面有main方法

进入first-bevy文件夹, 执行cargo run, 控制台会打印hello world

一切准备就绪, 话不多说, 我们正式开始吧!

正文

完整可运行的游戏代码如下所示

use bevy::{
    // 由于是*, 所以会引入非常多bevy常用的东西
    prelude::*,
    // 导入bevy库的sprite模块中的Mesh2dHandle和MaterialMesh2dBundle结构体, 用于渲染小球
    sprite::{MaterialMesh2dBundle, Mesh2dHandle},
};


// 小球的颜色
const BALL_COLOR: Color = Color::rgb(0.5, 0.5, 1.0);

// 小球每次移动的步长(像素)
const MOVE_STEP: f32 = 5.0;

// 定义一个名为Player的组件结构体,也就是我们操作的球体.
// 这种没有内容的结构体Component, 在bevy中被称为Marker Component, 通常用于标记一个Entity
#[derive(Component)]
struct Player;

fn main() {
    // App是bevy引擎程序的总入口
    App::new()
        // 添加默认插件
        .add_plugins(DefaultPlugins)
        // add_systems向App中添加一个系统System
        // setup函数会在App初始化时被调度一次, 后续都不会再被调度
        // 类似Startup的调度类型在bevy中被称为Schedule
        .add_systems(Startup, setup)
        // 添加更新时的player_move
        .add_systems(Update, player_move)
        .run();
    
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    // 在场景中生成一个2d相机
    commands.spawn(Camera2dBundle::default());

    // 创建一个圆形的网格, 并获取其句柄
    let mesh = Mesh2dHandle(meshes.add(Circle {radius: 50.0}));

    // 在场景中生成一个小球实体, 使用MaterialMeshe2dBundle包装网格和材质
    commands.spawn((
        MaterialMesh2dBundle{
            mesh: mesh,
            material: materials.add(BALL_COLOR),
            ..default()
        },
        Player,
    ));

}

fn player_move(
    key_input: Res<ButtonInput<KeyCode>>,
    mut query: Query<(&Player, &mut Transform)>,
) {
    // 获取玩家实体的Player组件和Transform组件的可变引用.
    // Transform用来存储小球的形态, 位置等数据的bevy原生Component
    let (_, mut transform) = query.single_mut();


    // 如果按下W键, 向上移动小球
    if key_input.pressed(KeyCode::KeyW) {
        transform.translation.y += MOVE_STEP;
        println!("Up translation: {:?}", transform.translation);
    }
    
    // 向下移动
    if key_input.pressed(KeyCode::KeyS) {
        transform.translation.y -= MOVE_STEP;
        println!("Down translation: {:?}", transform.translation);
    }

    // 向右移动
    if key_input.pressed(KeyCode::KeyD) {
        transform.translation.x += MOVE_STEP;
        println!("Right translation: {:?}", transform.translation);
    }

    // 向左移动
    if key_input.pressed(KeyCode::KeyA) {
        transform.translation.x -= MOVE_STEP;
        println!("Left translation: {:?}", transform.translation);
    }
}

// fn hello_world_system() {
//     // ! 符号用于调用宏(Macro),它是 Rust 中用于调用宏的语法
//     println!("hello world")
// }

fn setup函数只会在游戏初始化(Startup)时调用, 后续都不会再调用它. 该函数内有三个参数:

  • mut commands: Commands: 这是一个 Commands 类型的可变引用,用于执行一系列的命令来创建、修改或删除 ECS(Entity-Component-System)中的实体、组件等. ECS做游戏的小伙伴们肯定很熟悉, 它与MVC类似, 是种架构模式, 但是它是面向数据的编程. 举个简单的例子, 张三是个Entity, 他有姓名、性别、从事的工作..., 这些都是他的数据, 我们将这些数据存放于Component中, 若哪天某企业需要做张三的背景调查, 则需要使用System查询到张三的数据信息.

  • mut meshes: ResMut<Assets<Mesh>>: 这是一个 ResMut<Assets> 类型的可变引用,用于管理和访问 Mesh 类型的资源。在 Rust 中,ResMut 用于表示可变引用,Assets 表示一种资源类型,用于存储和管理 Mesh 实例

  • mut materials: ResMut<Assets<ColorMaterial>>: 这是一个 ResMut<Assets> 类型的可变引用,用于管理和访问 ColorMaterial 类型的资源。类似地,Assets 表示一种资源类型,用于存储和管理 ColorMaterial 实例

fn setup使用command创建了小球实体, 使用 meshes 来加载和管理网格资源,使用 materials 来加载和管理材质资源

效果图

image.png

我们可以按WASD控制它的上左右下移动, 感兴趣的小伙伴们赶紧动手试试吧!