Bevy中的资源是用来实现存储某种数据类型单个全局实例的方式,它和Entity最大的不同就是,资源是全局的。通常,游戏中的配置或者设置项,都可以用Resource来实现。
创建资源
想要创建新的资源类型,派生Resource
trait
即可:
#[derive(Resource)]
struct GoalsReached {
main_goal: bool,
bonus: u32,
}
使用资源的时候,同一个类型只能有一个实例。如果想要有多个实例,可能Entity会更合适。
访问资源
如果要从System中访问资源,需要使用Res
或者ResMut
:
fn my_system(
// 获得 GoalsReached 的可变访问,如果不存在会 panic
mut goals: ResMut<GoalsReached>,
// 获得 MyOtherResource 的只读访问,如果不存在会 panic
other: Res<MyOtherResource>,
// 使用 Option 防止 MyFancyResource 不存在导致的 panic 问题
mut fancy: Option<ResMut<MyFancyResource>>,
) {
if let Some(fancy) = &mut fancy {
// 如果 MyFancyResource 存在
}
}
管理资源
如果我们需要在运行时增加或者删除资源,可以使用Commands
:
fn my_setup(mut commands: Commands) {
// 添加一个资源,如果资源已经存在,会被覆盖
commands.insert_resource(GoalsReached { main_goal: false, bonus: 100 });
// 如果资源不存在,会插入,如果存在,会忽略,资源的默认值参考 Default 实现
commands.init_resource::<MyFancyResource>();
// 移除资源
commands.remove_resource::<MyOtherResource>();
}
当然,我们可以在创建App
的时候,初始化资源,这样在App
的整个生命周期,资源都会存在。
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(StartingLevel(3))
.init_resource::<MyFancyResource>()
// ...
初始化资源
在使用init_resource
初始化资源的时候,我们需要实现Default
trait
来给资源提供默认值:
// 直接 derive,每个值均会使用其默认值,rust中很多类型都已经实现了 Default trait
#[derive(Resource, Default)]
struct GameProgress {
game_completed: bool,
secrets_unlocked: u32,
}
#[derive(Resource)]
struct StartingLevel(usize);
// 实现 Default trait 来自定义默认值
impl Default for StartingLevel {
fn default() -> Self {
StartingLevel(1)
}
}
如果实现逻辑比较复杂,可以考虑使用FromWorld
:
#[derive(Resource)]
struct MyFancyResource { /* code */ }
impl FromWorld for MyFancyResource {
fn from_world(world: &mut World) -> Self {
// 可以通过 world 访问别的资源
{
let mut x = world.resource_mut::<MyOtherResource>();
x.do_mut_stuff();
}
// 可以记载 asset
let font: Handle<Font> = world.resource::<AssetServer>().load("myfont.ttf");
MyFancyResource { /* code */ }
}
}
这里需要注意,过多的使用FromWorld
会造成代码的混乱,因为如果一个资源的初始化依赖别的资源或者逻辑,必然会造成一条依赖链路,这样在修改其他资源或者逻辑的时候,会影响整个链路。
使用建议
- 通常,游戏的全局设置会是使用
Resource
的一个不错的选择。 - 可能有时候对于使用实体或者资源不是很明确,一般我们有这样一个原则:如果是全局访问,则使用资源;如果是通过ECS系统访问,则使用实体。即使游戏中只有一件事(例如单人游戏中的玩家),使用实体可能很合适,而不是资源,因为实体由多个组件组成,其中一些可能与其他实体通用。这可以使游戏逻辑更灵活。例如,游戏角色中“生命值/伤害等”,这些在玩家和敌人中都会使用。