Rust游戏开发库bevy_asset_loader的使用:简化资源加载流程,优化Bevy引擎资产管理
Rust游戏开发库bevy_asset_loader的使用:简化资源加载流程,优化Bevy引擎资产管理
基本介绍
bevy_asset_loader是一个Bevy引擎插件,通过可衍生的AssetCollection
特性减少了游戏资产管理的样板代码。它可以自动加载实现了该特性的结构体,这些资产集合包含对游戏资产的句柄,并在加载后作为资源提供给系统使用。
加载状态的使用
加载状态负责在可配置的Bevy状态中管理加载过程。以下是一个基本示例:
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_state::<MyStates>()
.add_loading_state(
LoadingState::new(MyStates::AssetLoading)
.continue_to_state(MyStates::Next)
.load_collection::<AudioAssets>(),
)
.add_systems(OnEnter(MyStates::Next), start_background_audio)
.run();
}
#[derive(AssetCollection, Resource)]
struct AudioAssets {
#[asset(path = "audio/background.ogg")]
background: Handle<AudioSource>,
}
fn start_background_audio(mut commands: Commands, audio_assets: Res<AudioAssets>) {
commands.spawn((AudioPlayer(audio_assets.background.clone()), PlaybackSettings::LOOP));
}
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
enum MyStates {
#[default]
AssetLoading,
Next,
}
动态添加资产集合
我们可以在应用程序的任何位置使用configure_loading_state
向加载状态添加额外的资产集合:
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
struct PlayerPlugin;
impl Plugin for PlayerPlugin {
fn build(&self, app: &mut App) {
app
.configure_loading_state(
LoadingStateConfig::new(MyStates::AssetLoading)
.load_collection::<PlayerAssets>(),
);
}
}
#[derive(AssetCollection, Resource)]
struct PlayerAssets {
#[asset(path = "images/player.png")]
sprite: Handle<Image>,
}
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
enum MyStates {
#[default]
AssetLoading,
Next,
}
完整示例代码
以下是一个完整的示例,展示了如何使用bevy_asset_loader加载不同类型的资产:
use bevy::prelude::;*
use bevy_asset_loader::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_state::<GameState>()
.add_loading_state(
LoadingState::new(GameState::Loading)
.continue_to_state(GameState::Playing)
.load_collection::<GameAssets>()
)
.add_systems(OnEnter(GameState::Playing), setup_game)
.run();
}
#[derive(AssetCollection, Resource)]
struct GameAssets {
// 单个音频文件
#[asset(path = "audio/background.ogg")]
background_music: Handle<AudioSource>,
// 纹理图集
#[asset(texture_atlas_layout(tile_size_x = 64, tile_size_y = 64, columns = 8, rows = 1))]
atlas_layout: Handle<TextureAtlasLayout>,
#[asset(path = "images/spritesheet.png")]
spritesheet: Handle<Image>,
// 多个图像文件
#[asset(paths("images/player.png", "images/enemy.png"), collection(typed))]
character_images: Vec<Handle<Image>>,
// 动态资产
#[asset(key = "ui.font")]
font: Handle<Font>,
}
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
enum GameState {
#[default]
Loading,
Playing,
}
fn setup_game(
mut commands: Commands,
assets: Res<GameAssets>,
mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
) {
// 创建纹理图集
let atlas = TextureAtlasLayout::from_grid(
Vec2::new(64.0, 64.0),
8,
1,
None,
None
);
let atlas_handle = texture_atlases.add(atlas);
// 播放背景音乐
commands.spawn(AudioBundle {
source: assets.background_music.clone(),
settings: PlaybackSettings::LOOP,
});
// 使用加载的资产创建精灵
for (i, image) in assets.character_images.iter().enumerate() {
commands.spawn(SpriteBundle {
texture: image.clone(),
transform: Transform::from_xyz(i as f32 * 100.0, 0.0, 0.0),
..default()
});
}
}
动态资产配置
动态资产允许将资产配置存储在资产文件中,而不是硬编码在代码中:
#[derive(AssetCollection, Resource)]
struct ImageAssets {
#[asset(key = "player")]
player: Handle<Image>,
#[asset(key = "tree")]
tree: Handle<Image>,
}
对应的动态资产文件(.assets.ron):
({
"player": File (
path: "images/player.png",
),
"tree": File (
path: "images/tree.png",
),
})
不使用加载状态的用法
虽然加载状态模式很有用,但也可以在不使用加载状态的情况下使用bevy_asset_loader:
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_collection::<MyAssets>()
.run();
}
#[derive(AssetCollection, Resource)]
struct MyAssets {
#[asset(texture_atlas_layout(tile_size_x = 64, tile_size_y = 64, columns = 8, rows = 1))]
layout: Handle<TextureAtlasLayout>,
#[asset(path = "images/sprite_sheet.png")]
sprite: Handle<Image>,
}
支持的资产字段类型
bevy_asset_loader支持多种资产字段类型,包括:
- 单个文件资产(
Handle<T>
) - 纹理图集(需启用
2d
特性) - 标准材质(需启用
3d
特性) - 文件夹内容(不适用于Web构建)
- 路径列表
- 实现
FromWorld
的类型
进度跟踪
启用progress_tracking
特性后,可以与iyes_progress
集成,在加载状态期间跟踪资产加载进度,例如用于显示进度条。
许可证
bevy_asset_loader采用双重许可:
- MIT许可证
- Apache-2.0许可证
兼容性
bevy_asset_loader的主分支与最新的Bevy版本兼容。当前版本支持Bevy 0.16。
Rust游戏开发库bevy_asset_loader的使用指南
简介
bevy_asset_loader
是一个为Bevy游戏引擎设计的扩展库,旨在简化资源加载流程并优化资产管理。它提供了一种声明式的方式来定义和加载游戏资源,减少了手动管理资源状态的样板代码。
主要特性
- 简化资源加载状态管理
- 声明式资源定义
- 支持进度跟踪
- 与Bevy的ECS系统无缝集成
- 支持多种资源类型
安装
在Cargo.toml
中添加依赖:
[dependencies]
bevy = "0.10"
bevy_asset_loader = "0.17"
完整示例代码
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
// 定义游戏状态
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
enum GameState {
#[default]
Loading, // 加载状态
Menu, // 菜单状态
Game, // 游戏状态
}
// 定义游戏资源集合
#[derive(AssetCollection, Resource)]
struct GameAssets {
#[asset(path = "textures/player.png")]
player_texture: Handle<Image>,
#[asset(path = "sounds/background.ogg")]
background_music: Handle<AudioSource>,
#[asset(path = "fonts/arial.ttf")]
font: Handle<Font>,
}
// 定义菜单资源集合
#[derive(AssetCollection, Resource)]
struct MenuAssets {
#[asset(path = "textures/button.png")]
button_texture: Handle<Image>,
#[asset(path = "fonts/menu.ttf")]
menu_font: Handle<Font>,
}
fn main() {
App::new()
// 添加默认插件
.add_plugins(DefaultPlugins)
// 设置加载状态
.add_loading_state(
LoadingState::new(GameState::Loading)
.continue_to_state(GameState::Menu)
// 加载游戏资源
.load_collection::<GameAssets>()
// 加载菜单资源
.load_collection::<MenuAssets>()
// 添加进度跟踪
.with_progress(Progress::new(ProgressCounter::default()))
)
// 添加游戏状态
.add_state::<GameState>()
// 添加系统
.add_systems(OnEnter(GameState::Menu), setup_menu)
.add_systems(OnEnter(GameState::Game), setup_game)
.add_systems(Update, handle_menu_input)
.run();
}
// 菜单系统
fn setup_menu(
mut commands: Commands,
menu_assets: Res<MenuAssets>,
) {
commands.spawn(Camera2dBundle::default());
// 创建按钮
commands.spawn(SpriteBundle {
texture: menu_assets.button_texture.clone(),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
});
// 创建菜单文本
commands.spawn(Text2dBundle {
text: Text::from_section(
"Press SPACE to Start",
TextStyle {
font: menu_assets.menu_font.clone(),
font_size: 30.0,
color: Color::WHITE,
},
),
transform: Transform::from_xyz(0.0, -100.0, 0.0),
..default()
});
}
// 游戏系统
fn setup_game(
mut commands: Commands,
game_assets: Res<GameAssets>,
) {
commands.spawn(Camera2dBundle::default());
// 创建玩家精灵
commands.spawn(SpriteBundle {
texture: game_assets.player_texture.clone(),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
});
// 创建游戏文本
commands.spawn(Text2dBundle {
text: Text::from_section(
"Hello Bevy!",
TextStyle {
font: game_assets.font.clone(),
font_size: 60.0,
color: Color::WHITE,
},
),
transform: Transform::from_xyz(0.0, 200.0, 0.0),
..default()
});
}
// 处理菜单输入
fn handle_menu_input(
keyboard_input: Res<Input<KeyCode>>,
mut next_state: ResMut<NextState<GameState>>,
) {
if keyboard_input.just_pressed(KeyCode::Space) {
next_state.set(GameState::Game);
}
}
最佳实践
- 按功能模块组织资源集合,而不是将所有资源放在一个集合中
- 为不同的游戏状态(菜单、游戏、结束等)使用不同的资源集合
- 对于大型资源,考虑使用动态加载和卸载
- 利用进度跟踪提供加载反馈给玩家
总结
bevy_asset_loader
通过提供声明式的资源管理方式,大大简化了Bevy游戏开发中的资源加载流程。它减少了手动状态管理的代码量,使开发者能够更专注于游戏逻辑的实现。