资源管理

概述

学习目标

  • 理解资源管理系统的基本概念
  • 掌握资源类型的定义和使用
  • 学会加载资源(同步、异步、预加载、热重载)
  • 了解资源管理(句柄、生命周期、清理、缓存)

前置知识要求

  • Bevy 快速入门
  • ECS 基础
  • 资源(Resources)基础

核心概念

什么是资源管理?

资源管理是 Bevy 中用于加载和管理游戏资源的功能。Bevy 的资源管理系统支持多种资源类型,包括模型、纹理、音频、字体等。

为什么需要资源管理?

  1. 资源加载:游戏需要加载各种资源,如模型、纹理、音频等
  2. 异步加载:资源加载是异步的,不会阻塞游戏运行
  3. 资源缓存:资源管理系统会自动缓存已加载的资源
  4. 热重载:支持热重载,可以在运行时重新加载资源

资源管理的核心组件

Bevy 资源管理系统包含以下核心组件:

  • AssetServer:资源服务器,用于加载资源
  • Assets:资源集合,存储已加载的资源
  • Handle:资源句柄,用于引用资源
  • AssetLoader:资源加载器,用于加载特定类型的资源

基础用法

资源加载

加载资源的基本方法。

源代码文件bevy/examples/asset/asset_loading.rs

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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)] 定义资源类型
  • 实现 AssetLoader trait 来创建资源加载器
  • 在应用中初始化资源类型和加载器
  • 可以支持自定义文件格式

说明
自定义资源类型允许你创建自己的资源格式。这对于游戏特定的数据格式非常有用。

热重载

在运行时重新加载资源。

源代码文件bevy/examples/asset/hot_asset_reloading.rs

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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_watcher cargo feature
  • 适合开发时快速迭代

说明
热重载功能允许在开发时快速迭代。修改资源文件后,资源会自动重新加载,无需重启游戏。

嵌入资源

将资源嵌入到可执行文件中。

源代码文件bevy/examples/asset/embedded_asset.rs

关键信息

  • 可以使用 embedded_asset! 宏嵌入资源
  • 嵌入的资源包含在可执行文件中
  • 适合小型资源或需要快速加载的资源
  • 可以与其他资源加载方式结合使用

说明
嵌入资源允许将资源直接包含在可执行文件中。这对于小型资源或需要快速加载的资源非常有用。

额外资源源

从多个源加载资源。

源代码文件bevy/examples/asset/extra_source.rs

关键信息

  • 可以配置多个资源源
  • 可以从不同的目录或服务器加载资源
  • 可以设置资源源的优先级
  • 适合模块化资源管理

说明
额外资源源允许从多个位置加载资源。这对于模块化资源管理或从服务器加载资源非常有用。

实际应用

在游戏开发中的应用场景

资源管理在游戏开发中有广泛的应用:

  1. 模型加载:加载 3D 模型和场景
  2. 纹理加载:加载纹理和材质
  3. 音频加载:加载音频文件
  4. 字体加载:加载字体文件
  5. 配置文件:加载游戏配置数据

常见问题

问题 1:如何等待资源加载完成?

解决方案:可以监听 AssetEvent::LoadedWithDependencies 事件,或者使用 Assets<T>get() 方法检查资源是否已加载。

问题 2:如何管理资源的生命周期?

解决方案:资源的生命周期由句柄管理。只要还有句柄引用资源,资源就会保持在内存中。确保在不再需要资源时丢弃句柄。

问题 3:如何优化资源加载性能?

解决方案:可以使用资源预加载、资源缓存、资源压缩等技术来优化资源加载性能。

性能考虑

  1. 资源预加载:在需要之前预加载资源
  2. 资源缓存:合理使用资源缓存以减少重复加载
  3. 资源压缩:使用压缩格式减少资源大小
  4. 异步加载:利用异步加载避免阻塞游戏运行

相关资源

相关源代码文件

  • 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 - 额外资源源示例

官方文档链接

进一步学习建议

  • 学习场景系统,了解如何加载和管理场景
  • 学习资源管理,了解如何优化资源加载性能
  • 学习自定义资源类型,了解如何创建自己的资源格式

索引返回上级目录