资源
可参考的官方例子:
asset_loading
.
Bevy 有一个灵活的系统来异步加载和管理你的游戏资源(运行在后台,不会在你的游戏中造成尖峰时延)。
你加载的资源的数据被存储在 Assets<T>
资源 中(资源类型列表)。
资源是通过 句柄
的设计模式来追踪的。句柄仅仅只是特定资源的轻量级 ID。
使用 AssetServer 加载资源
要从文件中加载资源,请使用 AssetServer
。
struct UiFont(Handle<Font>);
fn load_ui_font(
mut commands: Commands,
server: Res<AssetServer>
) {
let handle: Handle<Font> = server.load("font.ttf");
// we can store the handle in a resource:
// - to prevent the asset from being unloaded
// - if we want to use it to access the asset later
commands.insert_resource(UiFont(handle));
}
这将使资源加载在后台排队进行。被加载的资源需要稍等一些时间才可以被使用。资源就绪之前,在同一系统中,你可以通过资源句柄来使用资源,但实际资源数据仍需等就绪后才能真正变得有效果。
在资产加载之前你就可以使用资源句柄来生成你的 2D 精灵、3D 模型和用户界面,当资源准备就绪时,它们就会 "跳进来"。
需要知道的是,你可以随意多次地调用 asset_server.load
来加载同一资源,即使资源目前正在加载或已经加载,都只会为你返回相同的句柄。如果资源处于不可用或未加载的状态,则它将会被重新开始加载。
创建你自己资源
你也可以 Assets<T>
来手动添加资源。
如果你想用代码来创建它们(比如程序化生成),或者你已经通过其他方式得到了数据,这种方式就会很有用。
fn add_material(
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let new_mat = StandardMaterial {
base_color: Color::rgba(0.25, 0.50, 0.75, 1.0),
unlit: true,
..Default::default()
};
let handle = materials.add(new_mat);
// do something with the handle
}
热重载
Bevy 可以监测到你何时修改了你的资源文件,并在游戏运行的过程中重新加载它们。更多信息请见本页面。
句柄
句柄(Handle<T>
)是引用特定资源的典型方式。当你在游戏中生成一些东西时,比如 2D 精灵、3D 模型或 UI,它们在各自的组件中将需要通过句柄来引用它们使用到的资源。
你可以把你的句柄存放在你认为方便的地方(比如 resources) 中),或者直接存放在实体的组件中。
如果你没有把句柄存储在任何地方,你仍可以通过调用 asset_server.load
从一个路径获得一个句柄。这样你就可以在需要访问资源的时候简单地调用,而不必费力地存储句柄。
访问资源
要从系统中访问实际资源数据,请使用 Assets<T>
resource。
你可以使用句柄或资源路径来获取你想要的资源:
struct SpriteSheets {
map_tiles: Handle<TextureAtlas>,
}
fn use_sprites(
handles: Res<SpriteSheets>,
atlases: Res<Assets<TextureAtlas>>,
images: Res<Assets<Image>>,
) {
// Could be `None` if the asset isn't loaded yet
if let Some(atlas) = atlases.get(&handles.map_tiles) {
// do something with the texture atlas
}
// Can use a path instead of a handle
if let Some(map_tex) = images.get("map.png") {
// if "map.png" was loaded, we can use it!
}
}
资源路径和标签
来自文件系统的资源可以通过 AssetPath
来识取,它由文件路径外加一个标签组成。标签用于区分同一文件中包含的多个资源。这方面的一个例子是 GLTF files 文件,它可以包含网格、场景、纹理、材质等等。
资源路径类型可以用一个路径字符串来初始化创建,标签(如果有的话)通过 #
号附在路径字符串之后。
fn load_gltf_things(
mut commands: Commands,
server: Res<AssetServer>
) {
// get a specific mesh
let my_mesh: Handle<Mesh> = server.load("my_scene.gltf#Mesh0/Primitive0");
// spawn a whole scene
let my_scene: Handle<Scene> = server.load("my_scene.gltf#Scene0");
commands.spawn_scene(my_scene);
}
关于使用 3D 模型的更多信息,请参阅 GLTF 页面。
句柄和资源生命周期(垃圾回收)
句柄有内置的引用计数(类似于 Rust 中 的 Rc
和 Arc
)。这使得 Bevy 可以跟踪一个资源并得知它是否仍然正在被引用,并在它不再被需要时自动卸载它。
你可以使用 .clone()
来为同一个资源创建多个句柄。句柄克隆是一个低消耗的操作,但它被设计为明确的显性调用,以确保你知道你的代码中哪些地方创建了额外的句柄,并可能影响资源的生命周期。
弱引用句柄
句柄可以是 "强引用"(默认)或 "弱引用"。只有强引用句柄被计算在资源引用计数中,并能维持资源的被加载状态。弱引用句柄可以让你引用一个资源,同时在该资源没有了强引用句柄后仍会被卸载(即使弱引用句柄仍存在)。
你需要使用 .clone_weak()
而不是 .clone()
来创建弱引用句柄。
非类型化的句柄
Bevy 还有一个 HandleUntyped
类型。如果你需要无视资源类型以能够引用任何资源,请使用这种类型的句柄。
这种类型允许你存储一个包含混合类型资源的集合(如 Vec
或 HashMap
)。
你可以使用 .clone_untyped()
创建一个非类型化的句柄。
非类型化资源加载
方便的是,如果你不知道文件是什么资源类型,AssetServer
支持非类型化加载。你也可以加载整个文件夹:
struct ExtraAssets(Vec<HandleUntyped>);
fn load_extra_assets(
mut commands: Commands,
server: Res<AssetServer>,
) {
if let Ok(handles) = server.load_folder("extra") {
commands.insert_resource(ExtraAssets(handles));
}
}
它将尝试根据文件扩展名来检测每个资产的格式。
资源事件
如果你需要在资源被创建、修改或移除时执行特定的业务逻辑,你可以通过关注 AssetEvent
事件 来做出相应的反应。
struct MyMapImage {
handle: Handle<Image>,
}
fn fixup_images(
mut ev_asset: EventReader<AssetEvent<Image>>,
mut assets: ResMut<Assets<Image>>,
map_img: Res<MyMapImage>,
) {
for ev in ev_asset.iter() {
match ev {
AssetEvent::Created { handle } |
AssetEvent::Modified { handle } => {
// a texture was just loaded or changed!
let texture = assets.get_mut(handle).unwrap();
// ^ unwrap is OK, because we know it is loaded now
if *handle == map_img.handle {
// it is our special map image!
} else {
// it is some other image
}
}
AssetEvent::Removed { handle } => {
// an image was unloaded
}
}
}
}