1 个不稳定版本
| 0.1.0 | 2024年3月23日 |
|---|
#1127 在 游戏开发
34KB
67 行
文件 Bevy插件
这是一个从Bevy游戏引擎插件系统中移除更多样板代码的实验/项目。虽然插件系统本身没有太多样板代码,但这是一个使用属性宏进一步减少其数量的实验。这是通过将 plugin 属性宏应用于您的代码中的 mod 模块来实现的,这样 mod 块实际上被移除,并生成了一个插件。下面是一个例子
// Create a plugin named `TestPlugin` (snake case names of the mod is turned into cammel case).
#[plugin]
mod test_plugin {
// This system will be run in the `Startup` schedule.
#[startup]
fn setup() { some system ... }
}
标记属性
系统、函数、结构和枚举可以通过属性(如上面所示的 #[startup])来标记,这些属性应用各种插件相关功能。
启动和更新系统
Startup 和 Update 调度对于Bevy插件至关重要。因此,要向这些调度中添加系统,只需用 #[startup] 或 #[update] 标记它们即可。
// Create a plugin named `TestPlugin`.
#[plugin]
mod test_plugin {
// This system will be run in the `Startup` schedule.
#[startup]
fn setup() { some system ... }
// This system will be run in the `Update` schedule.
#[update]
fn update() { some system ... }
}
在进入/退出状态时运行系统
能够在从中心到Bevy的状态进入和退出时运行系统。通常这可以通过使用OnEnter(<某个状态>)和OnExit(<某个状态>)调度来实现。这可以通过模块插件通过将以下内容应用于您的系统#[enter(<某个状态>)]或#[exit(<某个状态>)]来实现,就像您可以使用上面的#[startup]或#[update]那样。
事件系统运行
在使用Bevy时,当事件被触发时运行某事是很常见的。这个标记属性可以用来在事件被触发时运行一个系统。这个属性是#[event(<某种事件类型>)]。这些系统将自动读取Res<Current<the same event type>>资源,这个资源暂时添加到世界中以便运行这些系统,这样这些系统就可以访问被触发的事件。
// Create a plugin named `TestPlugin`.
#[plugin]
mod test_plugin {
// This system is run every time a `KeyboardInput` is fired.
#[event(KeyboardInput)]
fn keyboard_input() {
println!("Input {current:?}");
}
}
资源初始化工厂和系统
Bevy插件也负责初始化资源,虽然您可以按照我们稍后讨论的默认实现来初始化资源,但插件宏提供了两个标记属性,可以用来初始化这些资源。
第一个是#[resource_factory],允许您标记一个无输入并返回创建的资源的功能。这个函数在插件构建时运行,并将返回的资源添加到应用中。以下是一个例子
#[plugin]
mod test_plugin {
// When the plugin is built, this creates and adds `ResourceB` to the app.
#[resource_factory]
fn create_b() -> ResourceB { ResourceB(2) }
}
第二个是#[resource_system],允许您标记一个返回资源的系统。该系统在启动时运行,并将返回的资源添加到世界中。以下是一个例子
#[plugin]
mod test_plugin {
// On `Startup`, this creates and adds `ResourceC` to the app.
#[resource_system]
fn create_resource(
mut commands: Commands,
a: Res<ResourceA>,
b: Res<ResourceB>
) -> ResourceC {
// camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
ResourceC(a.0 + b.0)
}
}
构建函数
然而,有时您可能需要在插件构建时访问应用,就像您使用常规Bevy插件那样。您可以通过标记一个返回无内容并接受对App的可变引用的函数,并用#[build]标记该函数来实现。以下是如何做到这一点的例子
#[plugin]
mod test_plugin {
// This is run when the plugin is built.
#[build]
fn on_build(app: &mut App) {
... do whatever with the app
}
}
自动初始化事件
插件需要能够将其事件添加到 App 中。您可以通过在创建于 #[plugin] 模块中的事件上添加 #[init_event] 标记属性来实现。当插件构建时,事件将由插件添加到 App。
#[plugin]
mod test_plugin {
#[init_event]
#[derive(Event)]
pub struct SomeEvent(pub i32);
}
自动初始化资源
资源可以选择由它们的 Default 实现进行初始化。为此,只需将创建于标记为 #[plugin] 的模中的资源标记为 #[init_resource]。然后,当插件构建时,资源将通过它们的 Default 实现添加到 App。
#[plugin]
mod test_plugin {
#[init_resource]
#[derive(Resource)]
pub struct SomeResource {
name: String,
num: i32
}
}
自动初始化状态
插件还需要在构建时将 State 添加到 App 中。您可以通过两种方式完成此操作:通过 State 的 Default 实现或指定一个起始 State。您可以通过在具有 Default 实现的 State 上标记 #[init_state] 来完成此操作。否则,您需要通过在 State 上标记 #[init_state(State::Kind)] 来指定起始 State。
#[plugin]
mod test_plugin {
#[init_state]
#[derive(States, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum ExampleState {
#[default]
StateA,
StateB
}
----------- OR -----------
#[init_state(ExampleState::StateA)]
#[derive(States, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum ExampleState {
StateA,
StateB
}
}
注册类型
对于反射,您需要将类型注册到 App 中。通常,您会在插件构建时通过在 App 上调用 register_type 函数来实现此操作。您可以在 #[plugin] 模中通过标记一个要注册的结构来执行此操作,使用 #[register] 属性标记。
#[plugin]
mod test_plugin {
#[register]
pub struct SomeType(pub u32);
}
可执行文件
将系统添加到数据,如结构体或枚举中可能非常有用。这对于泛型类型很有用,其中可能需要根据实际类型运行不同的系统。例如,服务器向客户端发送一个“操作”,然后客户端运行系统来应用该“操作”。以下是如何将结构体变成一个“可执行”结构体的示例
pub struct ExecutableData {
name: String,
data: i32
}
#[executable(ExecutableData)]
fn execute_data() { ... some system }
要执行上述系统,我可以创建一个 ExecutableData 的实例,并调用为 ExecutableData 实现的 execute 函数,该函数接收一个对 World 的可变引用。这将在给定的世界中运行该系统。
fn use_executable(world: &mut World, data: ExecutableData) {
data.execute(world);
}
依赖项
~19–47MB
~727K SLoC