Latest commit 4c0b78b
资产
相关的官方文档:asset_loading
Bevy用一个灵活的系统来异步加载和管理你的游戏资产(在后台,不会造成游戏的滞后高峰)。
加载资产时获得的数据存储在Assets<T>资源。
Bevy用句柄来跟踪资产。句柄只是特定资产的轻量级ID。
用AssetServer加载资产
用AssetServer资源从文件中加载资产。
#![allow(unused)] fn main() { struct UiFont(Handle<Font>); fn load_ui_font( mut commands: Commands, server: Res<AssetServer> ) { let handle: Handle<Font> = server.load("font.ttf"); // 可以把句柄存储在资源中,既可以防止资产被卸载,又能在以后访问 commands.insert_resource(UiFont(handle)); } }
这会使资产加载在后台进行排队,所以资产将需要一些时间才能使用。不能在相同的系统中立即访问实际数据,但是可以使用句柄。
可以在资产加载之前,就使用句柄生成2D精灵、3D模型和UI,当资产准备好后它们就会“弹”进来。
请注意,可以多次调用asset_server.load,即使资产正在加载或已经加载。它只是提供相同的句柄。如果资产不可用,它会开始加载。[TODO:不理解]
创建自定义资产
可以手动添加资产到Assets<T>。
Assets<T>可以用代码来创建资产(比如用于程序生成),或者用某些数据来创建资产。
#![allow(unused)] fn main() { fn add_material( mut material: ResMut<Assets<StandardMaterial>>, ) { let new_mat = StandardMaterial { base_color: Color::rgba(0.25, 0.50, 0.75, 1.0), unlit: true, ..Default::default() }; material.add(new_mat); } }
热加载
Bevy可以实时检测资源文件的改变,在游戏运行时重载资源。查看[Hot-Reloading Assets]。
句柄
句柄是引用特定资产的典型方法。 把事物(例如2D精灵,3D模型或UI)生成到游戏中时,它们各自的组件会需要它们所用资产的句柄。
可以把句柄存储在方便使用的地方(比如[资源])。
如果没有在任何地方存储句柄,可以通过调用asset_server.load从路径中生成句柄。如果不需要存储句柄,可以随时随地生成句柄。
知识点:
访问资产
用Assets<T>资源从系统中访问实际资产数据。
可以使用句柄或资产路径来标记所需的资产:
#![allow(unused)] fn main() { struct SpriteSheets { map_tiles: Handle<TextureAtlas>, } fn use_sprites( handles: Res<SpriteSheets>, atlases: Res<Assets<TextureAtlas>>, textures: Res<Assets<Texture>>, ) { // 如果资产未加载,返回`None` if let Some(atlas) = atlases.get(&handles.map_tiles) { // 做一些事 } // 可以用路径代替句柄 if let Some(map_tex) = textures.get("map.png") { // 做一些事 } } }
资产路径和标签
文件系统中的资产可以通过AssetPath来标记,AssetPath由文件路径+标签组成。标签用于同一文件中包含多个资产的情况。一个例子是GLTF文件,它可以包含meshes、场景、纹理、材质等。
资产路径可以由一个字符串创建,并在#符号后附加标签(如果有)。
#![allow(unused)] fn main() { fn load_gltf_things( mut commands: Commands, server: Res<AssetServer> ) { // 获取特定的网格 let my_mesh: Handle<Mesh> = server.load("my_scene.gltf#Mesh0/Primitive0"); // 生成整个场景 let my_scene: Handle<Scene> = server.load("my_scene.gltf#Scene0"); commands.spawn_scene(my_scene); } }
(对于GLTF,Bevy会根据文件中每个对象的索引生成像Scene0这样的标签,但如果GLTF文件包括从3D建模软件导出的名称/标签,Bevy也会提供这些标签) 有关使用三维模型的更多信息,请查看[GLTF]页面。
句柄和资产生命周期(垃圾收集)
句柄有内置的引用计数(类似于Rust中的Rc/Arc)。这使得Bevy可以跟踪一个资产是否还需要,并在不再需要时自动卸载。
可以使用.clone()来创建同一资产的多个句柄。克隆是一种廉价的操作,但它是显式的,以确保你知道代码中哪些地方会创建额外的句柄,并可能影响资产的生命周期。
弱句柄
句柄可以是“强”(默认)或“弱”。只计算强句柄并导致资产保持加载状态。弱句柄可以引用资产,同时允许它在没有强句柄时进行垃圾收集。
可以使用.clone_weak()代替.clone()来创建弱句柄。
无类型句柄
Bevy有HandleUntyped类型,用来引用任何类型的资产。
这允许存储一个包含混合类型资产的集合(如Vec或HashMap)。
用.clone_untyped()创建一个无类型句柄。
无类型加载
方便的是,如果不知道文件是什么资产类型,AssetServer支持无类型化加载。可以加载整个文件夹:
#![allow(unused)] fn main() { struct ExtraAssets(Vec<HandleUntyped>); fn load_extra_assets( mut commands: Commands, server: Res<AssetServer>, ) { if let Ok(handle) = server.load_folder("extra") { commands.insert_resource(ExtraAssets(handles)); } } }
它会尝试根据文件扩展名来检测每个资产的格式。[TODO:“它”指什么?]
资产事件
可以用AssetEvent在资产完成加载、修改或删除时执行特定的操作。
#![allow(unused)] fn main() { struct MapTexture { handle: Handle<Texture>, } fn fixup_textures( mut ev_asset: EventReader<AssetEvent<Texture>>, mut assets: ResMut<Assets<Texture>>, map_tex: Res<MapTexture>, ) { for ev in ev_asset.iter() { match ev { AssetEvent::Created { handle } | AssetEvent::Modified { handle} => { // 刚刚加载或更改了纹理 let texture = assets.get_mut(handle).unwrap(); // ^ unwrap返回Ok,因为当前已经加载成功 if *handle == map_tex.handle { // 这是特殊的地图纹理 } else { // 这是其他纹理 } } AssetEvent::Removed { handle} => { // 纹理被卸载 } } } } }