这篇文章包括来自 Alice Cecile Bevy 的贡献者 的帮助和评论。 对 Bevy Discord 的补充。 非常感谢 Joy and Logic
Bevy 是一个用 Rust 编写的游戏引擎,以非常符合人体工程学的实体组件系统而闻名。
在 ECS 模式 中,实体是由组件组成的独特事物,就像游戏世界中的对象一样。 系统处理这些实体并控制应用程序的行为。 Bevy 的 API 如此优雅的原因在于,用户可以在 Rust 中编写常规函数,并且 Bevy 将知道如何通过类型签名调用它们,调度正确的数据。
已经有大量关于如何使用 ECS 模式来构建您自己的游戏的文档,例如 在非官方 Bevy Cheat Book 中。 相反,在本文中,我们将解释如何在 Bevy 本身中实现 ECS 模式。 为此,我们将从头开始构建一个类似于 Bevy 的小型 API,它可以接受任意系统函数。
这种模式非常通用,你可以将它应用到你自己的 Rust 项目中。 为了说明这一点,我们将在文章的最后一节中更详细地介绍 Axum Web 框架如何将这种模式用于其路由处理程序方法。
如果您熟悉 Rust 并对类型系统技巧感兴趣,那么本文适合您。 在开始之前,我建议您查看我之前关于 Bevy 标签实现的 文章。 让我们开始吧!
目录
-
Bevy 的系统功能类似于面向用户的 API
-
add_system方法
-
将功能添加为系统
-
插曲:运行一个例子
-
带参数的系统函数
-
存储通用系统
-
装箱我们的泛型
-
获取参数
-
相同的模式,不同的框架:Axum 中的提取器
Bevy 的系统功能类似于面向用户的 API
首先,让我们学习如何使用 Bevy 的 API,以便我们可以从它向后工作,自己重新创建它。 下面的代码显示了一个带有示例系统的小型 Bevy 应用程序:
use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) // includes rendering and keyboard input .add_system(move_player) // this is ours // in a real game you'd add more systems to e.g. spawn a player .run(); } #[derive(Component)] struct Player; /// Move player when user presses space fn move_player( // Fetches a resource registered with the `App` keyboard: Res<Input<KeyCode>>, // Queries the ECS for entities mut player: Query<(&mut Transform,), With<Player>>, ) { if !keyboard.just_pressed(KeyCode::Space) { return; } if let Ok(player) = player.get_single_mut() { // destructure the `(&mut Transform,)` type from above to access transform let (mut player_position,) = player; player_position.translation.x += 1.0; } }
在上面的代码中,我们可以将一个常规的 Rust 函数传递给 add_system,而 Bevy 知道如何处理它。 更好的是,我们可以使用函数参数告诉 Bevy 我们要查询哪些组件。 在我们的例子中,我们想要 Transform来自每个也有习惯的实体 Player零件。 在幕后,Bevy 甚至根据函数签名推断出哪些系统可以并行运行。
add_system方法
Bevy 有很多 API 表面。 毕竟,它是一个完整的游戏引擎,除了它的实体组件系统之外,还有一个调度系统、一个 2D 和 3D 渲染器等等。 在本文中,我们将忽略其中的大部分内容,而只关注将函数添加为系统并调用它们。
按照 Bevy 的示例,我们将调用我们添加系统的项目, App,并给它两种方法, new和 add_system:
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →
struct App { systems: Vec<System>, } impl App { fn new() -> App { App { s