docs/bevy/examples/asset/custom_asset.rs 4.1 K · 159 lines · raw · history

1 //! Implements loader for a custom asset type.
2
3 use bevy::{
4 asset::{io::Reader, AssetLoader, LoadContext},
5 prelude::*,
6 reflect::TypePath,
7 };
8 use serde::Deserialize;
9 use thiserror::Error;
10
11 #[derive(Asset, TypePath, Debug, Deserialize)]
12 struct CustomAsset {
13 #[expect(
14 dead_code,
15 reason = "Used to show how the data inside an asset file will be loaded into the struct"
16 )]
17 value: i32,
18 }
19
20 #[derive(Default, TypePath)]
21 struct CustomAssetLoader;
22
23 /// Possible errors that can be produced by [`CustomAssetLoader`]
24 #[non_exhaustive]
25 #[derive(Debug, Error)]
26 enum CustomAssetLoaderError {
27 /// An [IO](std::io) Error
28 #[error("Could not load asset: {0}")]
29 Io(#[from] std::io::Error),
30 /// A [RON](ron) Error
31 #[error("Could not parse RON: {0}")]
32 RonSpannedError(#[from] ron::error::SpannedError),
33 }
34
35 impl AssetLoader for CustomAssetLoader {
36 type Asset = CustomAsset;
37 type Settings = ();
38 type Error = CustomAssetLoaderError;
39 async fn load(
40 &self,
41 reader: &mut dyn Reader,
42 _settings: &(),
43 _load_context: &mut LoadContext<'_>,
44 ) -> Result<Self::Asset, Self::Error> {
45 let mut bytes = Vec::new();
46 reader.read_to_end(&mut bytes).await?;
47 let custom_asset = ron::de::from_bytes::<CustomAsset>(&bytes)?;
48 Ok(custom_asset)
49 }
50
51 fn extensions(&self) -> &[&str] {
52 &["custom"]
53 }
54 }
55
56 #[derive(Asset, TypePath, Debug)]
57 struct Blob {
58 bytes: Vec<u8>,
59 }
60
61 #[derive(Default, TypePath)]
62 struct BlobAssetLoader;
63
64 /// Possible errors that can be produced by [`BlobAssetLoader`]
65 #[non_exhaustive]
66 #[derive(Debug, Error)]
67 enum BlobAssetLoaderError {
68 /// An [IO](std::io) Error
69 #[error("Could not load file: {0}")]
70 Io(#[from] std::io::Error),
71 }
72
73 impl AssetLoader for BlobAssetLoader {
74 type Asset = Blob;
75 type Settings = ();
76 type Error = BlobAssetLoaderError;
77
78 async fn load(
79 &self,
80 reader: &mut dyn Reader,
81 _settings: &(),
82 _load_context: &mut LoadContext<'_>,
83 ) -> Result<Self::Asset, Self::Error> {
84 info!("Loading Blob...");
85 let mut bytes = Vec::new();
86 reader.read_to_end(&mut bytes).await?;
87
88 Ok(Blob { bytes })
89 }
90 }
91
92 fn main() {
93 App::new()
94 .add_plugins(DefaultPlugins)
95 .init_resource::<State>()
96 .init_asset::<CustomAsset>()
97 .init_asset::<Blob>()
98 .init_asset_loader::<CustomAssetLoader>()
99 .init_asset_loader::<BlobAssetLoader>()
100 .add_systems(Startup, setup)
101 .add_systems(Update, print_on_load)
102 .run();
103 }
104
105 #[derive(Resource, Default)]
106 struct State {
107 handle: Handle<CustomAsset>,
108 other_handle: Handle<CustomAsset>,
109 blob: Handle<Blob>,
110 printed: bool,
111 }
112
113 fn setup(mut state: ResMut<State>, asset_server: Res<AssetServer>) {
114 // Recommended way to load an asset
115 state.handle = asset_server.load("data/asset.custom");
116
117 // File extensions are optional, but are recommended for project management and last-resort inference
118 state.other_handle = asset_server.load("data/asset_no_extension");
119
120 // Will use BlobAssetLoader instead of CustomAssetLoader thanks to type inference
121 state.blob = asset_server.load("data/asset.custom");
122 }
123
124 fn print_on_load(
125 mut state: ResMut<State>,
126 custom_assets: Res<Assets<CustomAsset>>,
127 blob_assets: Res<Assets<Blob>>,
128 ) {
129 let custom_asset = custom_assets.get(&state.handle);
130 let other_custom_asset = custom_assets.get(&state.other_handle);
131 let blob = blob_assets.get(&state.blob);
132
133 // Can't print results if the assets aren't ready
134 if state.printed {
135 return;
136 }
137
138 if custom_asset.is_none() {
139 info!("Custom Asset Not Ready");
140 return;
141 }
142
143 if other_custom_asset.is_none() {
144 info!("Other Custom Asset Not Ready");
145 return;
146 }
147
148 if blob.is_none() {
149 info!("Blob Not Ready");
150 return;
151 }
152
153 info!("Custom asset loaded: {:?}", custom_asset.unwrap());
154 info!("Custom asset loaded: {:?}", other_custom_asset.unwrap());
155 info!("Blob Size: {} Bytes", blob.unwrap().bytes.len());
156
157 // Once printed, we won't print again
158 state.printed = true;
159 }