关于 bevy-custom-assets
面向使用 Bevy 0.19 的开发者,说明自定义资源加载器的实现方式及版本变更注意事项,涵盖异步读取和依赖资源加载。
主要功能
- 01实现自定义资源加载器
- 02异步读取与随机访问
- 03通过 LoadContext 加载依赖
- 04适配 Bevy 0.19 API 变更
适用场景
- 01加载自定义二进制或文本格式
- 02构建依赖纹理等资源的关卡加载器
- 03迁移旧版嵌套加载代码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ chrisgliddon/ bevy-skills --skill bevy-custom-assets安装后在 Claude Code 中输入/bevy-custom-assets确认已加载。
详细介绍
Bevy 0.19 — 自定义资源加载器
何时使用此技能
- 加载自定义二进制或文本格式(专有游戏数据、配套配置等)。
- 生成依赖其他资源的资源(例如,会加载引用纹理的关卡加载器)。
- 将
LoadContext::loader()或旧版NestedLoader迁移到load_builder()/NestedLoadBuilder。 - 需要异步访问底层文件(
reader.read_to_end、reader.seekable())。 - 编译器错误:“
MyLoader: TypePathis not implemented”——0.18 要求加载器必须实现TypePath。
标准模式
use bevy::asset::io::Reader;
use bevy::asset::{Asset, AssetApp, AssetLoader, LoadContext};
use bevy::prelude::*;
use bevy::reflect::TypePath;
use serde::Deserialize;
use thiserror::Error;
#[derive(Asset, TypePath, Debug, Deserialize)]
pub struct LevelDef {
pub name: String,
pub gravity: f32,
pub thumbnail: String, // path to a referenced texture asset
}
#[derive(TypePath)] // 0.18: required on the loader itself.
pub struct LevelLoader;
#[derive(Debug, Error)]
pub enum LevelLoaderError {
#[error("io: {0}")]
Io(#[from] std::io::Error),
#[error("parse: {0}")]
Parse(#[from] ron::error::SpannedError),
}
impl AssetLoader for LevelLoader {
type Asset = LevelDef;
type Settings = ();
type Error = LevelLoaderError;
async fn load(
&self,
reader: &mut dyn Reader,
_settings: &Self::Settings,
load_context: &mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
// Pull the whole file. For very large files prefer `seekable()`.
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let level: LevelDef = ron::de::from_bytes(&bytes)?;
// Pull in a referenced asset so it loads alongside this one.
// The resulting handle ends up tracked as a dependency.
let _: Handle<Image> = load_context.load_builder().load(level.thumbnail.clone());
Ok(level)
}
fn extensions(&self) -> &[&str] {
&["level.ron"]
}
}
pub struct LevelLoaderPlugin;
impl Plugin for LevelLoaderPlugin {
fn build(&self, app: &mut App) {
app.init_asset::<LevelDef>()
.register_asset_loader(LevelLoader);
}
}
请求 Reader 提供随机访问能力
// Bevy 0.19 requires Reader implementations to expose `seekable()`.
# use bevy::asset::io::Reader;
# async fn _example(reader: &mut dyn Reader) -> std::io::Result<()> {
match reader.seekable() {
Ok(seekable) => {
// SeekableReader supports `seek(SeekFrom::Start(n))`, `seek(SeekFrom::End(-n))`, etc.
let _ = seekable;
}
Err(_) => {
// Source isn't seekable (e.g. HTTP backend). Fall back to streaming.
}
}
# Ok(())
# }
Bevy 0.19 注意事项
- 加载器结构体必须使用
#[derive(TypePath)](不能只在资源上使用)。0.18 强制要求这一点,以便反射加载器。否则会出现:“MyLoader: TypePathis not implemented”。 LoadContext::path()返回AssetPath,而不是&Path。若要获取平台路径,请对其调用.path():ctx.path().path()。LoadContext::asset_bytes()已移除。 在load异步函数中使用let mut bytes = Vec::new(); reader.read_to_end(&mut bytes).await?;,或使用reader.seekable()实现随机访问。- 必须通过
LoadContext加载依赖项。在加载器中调用asset_server.load(...)不会将结果注册为正在构建的资源的依赖项——请使用load_context.load_builder().load(...)(或便捷方法load(...)),以便正确触发AssetEvent::LoadedWithDependencies。 LoadContext::loader()已在 0.19 中替换。 请使用load_context.load_builder()。即时加载改为load_value,动态加载改为load_erased,未知类型加载则改为 builder 操作load_untyped。- 自定义
Reader实现必须在 0.19 中定义seekable()。 仅当该类型实现了AsyncSeek时才返回Ok(self);否则返回Err(ReaderNotSeekableError)。 AssetLoader是异步的,但不能在其中调用tokio::spawn——Bevy 使用自己的执行器,而不是 Tokio。对于计算密集型工作,请使用bevy::tasks::futures_lite或bevy::tasks::AsyncComputeTaskPool。- 资源类型必须使用
#[derive(Asset)],并结合TypePath。 资源的Settings类型必须满足Default + Serialize + DeserializeOwned + Send + Sync + 'static,才能参与.meta文件处理。 - 扩展名会按完整后缀匹配。
"level.ron"会匹配foo.level.ron,但不会匹配foo.ron——这有助于与通用 RON 格式区分。
另请参阅
bevy-assets— 加载完成后使用Handle<MyAsset>。bevy-voxel-data— 由 RON 驱动的资源模式。bevy-migration-0-17-to-0-18— 移除LoadContext::asset_bytes。bevy-migration-0-18-to-0-19— 嵌套加载 builder 与 Reader 变更。
评论
支持 Markdown