资源管理
概述
学习目标:
- 理解资源管理系统的基本概念
- 掌握资源类型的定义和使用
- 学会加载资源(同步、异步、预加载、热重载)
- 了解资源管理(句柄、生命周期、清理、缓存)
前置知识要求:
- Bevy 快速入门
- ECS 基础
- 资源(Resources)基础
核心概念
什么是资源管理?
资源管理是 Bevy 中用于加载和管理游戏资源的功能。Bevy 的资源管理系统支持多种资源类型,包括模型、纹理、音频、字体等。
为什么需要资源管理?
- 资源加载:游戏需要加载各种资源,如模型、纹理、音频等
- 异步加载:资源加载是异步的,不会阻塞游戏运行
- 资源缓存:资源管理系统会自动缓存已加载的资源
- 热重载:支持热重载,可以在运行时重新加载资源
资源管理的核心组件
Bevy 资源管理系统包含以下核心组件:
- AssetServer:资源服务器,用于加载资源
- Assets
:资源集合,存储已加载的资源 - Handle
:资源句柄,用于引用资源 - AssetLoader:资源加载器,用于加载特定类型的资源
基础用法
资源加载
加载资源的基本方法。
源代码文件:bevy/examples/asset/asset_loading.rs
代码示例:
use bevy::{asset::LoadedFolder, prelude::*};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
meshes: Res<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// 默认情况下,AssetServer 将从 "assets" 文件夹内加载资源
// 例如,下一行将加载 "ROOT/assets/models/cube/cube.gltf"
// 其中 "ROOT" 是应用程序的目录
//
// 这可以通过设置 [`AssetPlugin.file_path`] 来覆盖
let cube_handle = asset_server.load(
GltfAssetLabel::Primitive {
mesh: 0,
primitive: 0,
}
.from_asset("models/cube/cube.gltf"),
);
let sphere_handle = asset_server.load(
GltfAssetLabel::Primitive {
mesh: 0,
primitive: 0,
}
.from_asset("models/sphere/sphere.gltf"),
);
// 所有资源在加载完成后都会进入它们的 Assets<T> 集合
if let Some(sphere) = meshes.get(&sphere_handle) {
// 你可能会注意到这不会运行!这是因为资源在并行加载时不会阻塞
// 当资源加载完成时,它将出现在相关的 Assets<T> 集合中
info!("{:?}", sphere.primitive_topology());
} else {
info!("sphere 尚未加载");
}
// 你可以像这样加载文件夹中的所有资源。它们将在不阻塞的情况下并行加载
// LoadedFolder 资源保存文件夹中每个资源的句柄
// 这些都是 LoadedFolder 资源的依赖项,这意味着如果你想要等待文件夹中的所有资源加载
// 可以等待 LoadedFolder 资源触发 AssetEvent::LoadedWithDependencies
// 如果你想保持文件夹中的资源存活,请确保存储返回的句柄
let _loaded_folder: Handle<LoadedFolder> = asset_server.load_folder("models/torus");
// 如果你想要文件夹中特定资源的句柄,最简单的方法是调用 load
// 它不会再次加载
// LoadedFolder 资源最终也会保存资源的句柄,但等待它加载并找到正确的句柄需要更多工作
let torus_handle = asset_server.load(
GltfAssetLabel::Primitive {
mesh: 0,
primitive: 0,
}
.from_asset("models/torus/torus.gltf"),
);
// 你也可以直接将资源添加到它们的 Assets<T> 存储中
let material_handle = materials.add(StandardMaterial {
base_color: Color::srgb(0.8, 0.7, 0.6),
..default()
});
// 使用加载的资源创建实体
commands.spawn((
Mesh3d(torus_handle),
MeshMaterial3d(material_handle.clone()),
Transform::from_xyz(-3.0, 0.0, 0.0),
));
}关键要点:
- 使用
AssetServer加载资源 - 资源加载是异步的,不会阻塞游戏运行
- 使用
Assets<T>集合访问已加载的资源 - 可以使用
load_folder()加载整个文件夹的资源 - 可以使用
add()方法直接添加资源到集合中
说明:
资源加载是资源管理系统的基础。通过 AssetServer 加载资源,资源会在后台异步加载,不会阻塞游戏运行。加载完成后,资源会出现在相应的 Assets<T> 集合中。
资源句柄
使用资源句柄引用资源。
关键信息:
Handle<T>是资源的引用- 句柄可以在资源加载完成之前使用
- 句柄可以克隆,多个句柄可以引用同一个资源
- 资源只有在所有句柄都被丢弃后才会被卸载
说明: 资源句柄是资源管理系统的核心。它允许在资源加载完成之前就使用资源,并且可以安全地共享资源引用。
资源生命周期
理解资源的生命周期。
关键信息:
- 资源在加载时创建
- 资源在最后一个句柄被丢弃时卸载
- 资源可以被缓存以提高性能
- 资源可以被热重载
说明: 资源的生命周期由句柄管理。只要还有句柄引用资源,资源就会保持在内存中。当所有句柄都被丢弃时,资源会被卸载。
进阶用法
自定义资源类型
创建自定义资源类型。
源代码文件:bevy/examples/asset/custom_asset.rs
代码示例:
use bevy::{
asset::{io::Reader, AssetLoader, LoadContext},
prelude::*,
reflect::TypePath,
};
use serde::Deserialize;
#[derive(Asset, TypePath, Debug, Deserialize)]
struct CustomAsset {
value: i32,
}
#[derive(Default)]
struct CustomAssetLoader;
impl AssetLoader for CustomAssetLoader {
type Asset = CustomAsset;
type Settings = ();
type Error = CustomAssetLoaderError;
async fn load(
&self,
reader: &mut dyn Reader,
_settings: &(),
_load_context: &mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let custom_asset = ron::de::from_bytes::<CustomAsset>(&bytes)?;
Ok(custom_asset)
}
fn extensions(&self) -> &[&str] {
&["custom"]
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_asset::<CustomAsset>()
.init_asset_loader::<CustomAssetLoader>()
.run();
}关键要点:
- 使用
#[derive(Asset, TypePath)]定义资源类型 - 实现
AssetLoadertrait 来创建资源加载器 - 在应用中初始化资源类型和加载器
- 可以支持自定义文件格式
说明: 自定义资源类型允许你创建自己的资源格式。这对于游戏特定的数据格式非常有用。
热重载
在运行时重新加载资源。
源代码文件:bevy/examples/asset/hot_asset_reloading.rs
代码示例:
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// 加载我们的网格
let scene_handle =
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/torus/torus.gltf"));
// 对网格的任何更改都会自动重新加载!尝试修改 torus.gltf
// 你应该会立即看到更改出现在应用中
commands.spawn(SceneRoot(scene_handle));
}关键要点:
- 热重载允许在运行时修改资源文件
- 修改后的资源会自动重新加载
- 需要启用
file_watchercargo feature - 适合开发时快速迭代
说明: 热重载功能允许在开发时快速迭代。修改资源文件后,资源会自动重新加载,无需重启游戏。
嵌入资源
将资源嵌入到可执行文件中。
源代码文件:bevy/examples/asset/embedded_asset.rs
关键信息:
- 可以使用
embedded_asset!宏嵌入资源 - 嵌入的资源包含在可执行文件中
- 适合小型资源或需要快速加载的资源
- 可以与其他资源加载方式结合使用
说明: 嵌入资源允许将资源直接包含在可执行文件中。这对于小型资源或需要快速加载的资源非常有用。
额外资源源
从多个源加载资源。
源代码文件:bevy/examples/asset/extra_source.rs
关键信息:
- 可以配置多个资源源
- 可以从不同的目录或服务器加载资源
- 可以设置资源源的优先级
- 适合模块化资源管理
说明: 额外资源源允许从多个位置加载资源。这对于模块化资源管理或从服务器加载资源非常有用。
实际应用
在游戏开发中的应用场景
资源管理在游戏开发中有广泛的应用:
- 模型加载:加载 3D 模型和场景
- 纹理加载:加载纹理和材质
- 音频加载:加载音频文件
- 字体加载:加载字体文件
- 配置文件:加载游戏配置数据
常见问题
问题 1:如何等待资源加载完成?
解决方案:可以监听 AssetEvent::LoadedWithDependencies 事件,或者使用 Assets<T> 的 get() 方法检查资源是否已加载。
问题 2:如何管理资源的生命周期?
解决方案:资源的生命周期由句柄管理。只要还有句柄引用资源,资源就会保持在内存中。确保在不再需要资源时丢弃句柄。
问题 3:如何优化资源加载性能?
解决方案:可以使用资源预加载、资源缓存、资源压缩等技术来优化资源加载性能。
性能考虑
- 资源预加载:在需要之前预加载资源
- 资源缓存:合理使用资源缓存以减少重复加载
- 资源压缩:使用压缩格式减少资源大小
- 异步加载:利用异步加载避免阻塞游戏运行
相关资源
相关源代码文件:
bevy/examples/asset/asset_loading.rs- 资源加载示例bevy/examples/asset/custom_asset.rs- 自定义资源类型示例bevy/examples/asset/hot_asset_reloading.rs- 热重载示例bevy/examples/asset/embedded_asset.rs- 嵌入资源示例bevy/examples/asset/extra_source.rs- 额外资源源示例
官方文档链接:
进一步学习建议:
- 学习场景系统,了解如何加载和管理场景
- 学习资源管理,了解如何优化资源加载性能
- 学习自定义资源类型,了解如何创建自己的资源格式
索引:返回上级目录