1use bevy::{asset::LoadedFolder, image::ImageSampler, prelude::*};
11
12fn main() {
13 App::new()
14 .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .init_state::<AppState>()
16 .add_systems(OnEnter(AppState::Setup), load_textures)
17 .add_systems(Update, check_textures.run_if(in_state(AppState::Setup)))
18 .add_systems(OnEnter(AppState::Finished), setup)
19 .run();
20}
21
22#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, States)]
23enum AppState {
24 #[default]
25 Setup,
26 Finished,
27}
28
29#[derive(Resource, Default)]
30struct RpgSpriteFolder(Handle<LoadedFolder>);
31
32fn load_textures(mut commands: Commands, asset_server: Res<AssetServer>) {
33 commands.insert_resource(RpgSpriteFolder(asset_server.load_folder("textures/rpg")));
35}
36
37fn check_textures(
38 mut next_state: ResMut<NextState<AppState>>,
39 rpg_sprite_folder: Res<RpgSpriteFolder>,
40 mut events: MessageReader<AssetEvent<LoadedFolder>>,
41) {
42 for event in events.read() {
44 if event.is_loaded_with_dependencies(&rpg_sprite_folder.0) {
45 next_state.set(AppState::Finished);
46 }
47 }
48}
49
50fn setup(
51 mut commands: Commands,
52 rpg_sprite_handles: Res<RpgSpriteFolder>,
53 asset_server: Res<AssetServer>,
54 mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
55 loaded_folders: Res<Assets<LoadedFolder>>,
56 mut textures: ResMut<Assets<Image>>,
57) {
58 let loaded_folder = loaded_folders.get(&rpg_sprite_handles.0).unwrap();
59
60 let (texture_atlas_linear, linear_sources, linear_texture) = create_texture_atlas(
63 loaded_folder,
64 None,
65 Some(ImageSampler::linear()),
66 &mut textures,
67 );
68 let atlas_linear_handle = texture_atlases.add(texture_atlas_linear);
69
70 let (texture_atlas_nearest, nearest_sources, nearest_texture) = create_texture_atlas(
71 loaded_folder,
72 None,
73 Some(ImageSampler::nearest()),
74 &mut textures,
75 );
76 let atlas_nearest_handle = texture_atlases.add(texture_atlas_nearest);
77
78 let (texture_atlas_linear_padded, linear_padded_sources, linear_padded_texture) =
79 create_texture_atlas(
80 loaded_folder,
81 Some(UVec2::new(6, 6)),
82 Some(ImageSampler::linear()),
83 &mut textures,
84 );
85 let atlas_linear_padded_handle = texture_atlases.add(texture_atlas_linear_padded.clone());
86
87 let (texture_atlas_nearest_padded, nearest_padded_sources, nearest_padded_texture) =
88 create_texture_atlas(
89 loaded_folder,
90 Some(UVec2::new(6, 6)),
91 Some(ImageSampler::nearest()),
92 &mut textures,
93 );
94 let atlas_nearest_padded_handle = texture_atlases.add(texture_atlas_nearest_padded);
95
96 commands.spawn(Camera2d);
97
98 commands.spawn((
102 Sprite::from_image(linear_texture.clone()),
103 Transform {
104 translation: Vec3::new(-250.0, -160.0, 0.0),
105 scale: Vec3::splat(0.5),
106 ..default()
107 },
108 ));
109
110 commands.spawn((
112 Sprite::from_image(linear_padded_texture.clone()),
113 Transform {
114 translation: Vec3::new(250.0, -160.0, 0.0),
115 scale: Vec3::splat(0.5),
116 ..default()
117 },
118 ));
119
120 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
121
122 let text_style: TextFont = TextFont {
124 font: font.clone(),
125 font_size: 42.0,
126 ..default()
127 };
128
129 create_label(
133 &mut commands,
134 (-250.0, 250.0, 0.0),
135 "No padding",
136 text_style.clone(),
137 );
138
139 create_label(&mut commands, (250.0, 250.0, 0.0), "Padding", text_style);
141
142 let vendor_handle: Handle<Image> = asset_server
144 .get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png")
145 .unwrap();
146
147 let configurations: [(
149 &str,
150 Handle<TextureAtlasLayout>,
151 TextureAtlasSources,
152 Handle<Image>,
153 f32,
154 ); 4] = [
155 (
156 "Linear",
157 atlas_linear_handle,
158 linear_sources,
159 linear_texture,
160 -350.0,
161 ),
162 (
163 "Nearest",
164 atlas_nearest_handle,
165 nearest_sources,
166 nearest_texture,
167 -150.0,
168 ),
169 (
170 "Linear",
171 atlas_linear_padded_handle,
172 linear_padded_sources,
173 linear_padded_texture,
174 150.0,
175 ),
176 (
177 "Nearest",
178 atlas_nearest_padded_handle,
179 nearest_padded_sources,
180 nearest_padded_texture,
181 350.0,
182 ),
183 ];
184
185 let sampling_label_style = TextFont {
187 font,
188 font_size: 25.0,
189 ..default()
190 };
191
192 let base_y = 80.0; for (sampling, atlas_handle, atlas_sources, atlas_texture, x) in configurations {
195 create_sprite_from_atlas(
197 &mut commands,
198 (x, base_y, 0.0),
199 atlas_texture,
200 atlas_sources,
201 atlas_handle,
202 &vendor_handle,
203 );
204
205 create_label(
207 &mut commands,
208 (x, base_y + 110.0, 0.0), sampling,
210 sampling_label_style.clone(),
211 );
212 }
213}
214
215fn create_texture_atlas(
218 folder: &LoadedFolder,
219 padding: Option<UVec2>,
220 sampling: Option<ImageSampler>,
221 textures: &mut ResMut<Assets<Image>>,
222) -> (TextureAtlasLayout, TextureAtlasSources, Handle<Image>) {
223 let mut texture_atlas_builder = TextureAtlasBuilder::default();
225 texture_atlas_builder.padding(padding.unwrap_or_default());
226 for handle in folder.handles.iter() {
227 let id = handle.id().typed_unchecked::<Image>();
228 let Some(texture) = textures.get(id) else {
229 warn!(
230 "{} did not resolve to an `Image` asset.",
231 handle.path().unwrap()
232 );
233 continue;
234 };
235
236 texture_atlas_builder.add_texture(Some(id), texture);
237 }
238
239 let (texture_atlas_layout, texture_atlas_sources, texture) =
240 texture_atlas_builder.build().unwrap();
241 let texture = textures.add(texture);
242
243 let image = textures.get_mut(&texture).unwrap();
245 image.sampler = sampling.unwrap_or_default();
246
247 (texture_atlas_layout, texture_atlas_sources, texture)
248}
249
250fn create_sprite_from_atlas(
252 commands: &mut Commands,
253 translation: (f32, f32, f32),
254 atlas_texture: Handle<Image>,
255 atlas_sources: TextureAtlasSources,
256 atlas_handle: Handle<TextureAtlasLayout>,
257 vendor_handle: &Handle<Image>,
258) {
259 commands.spawn((
260 Transform {
261 translation: Vec3::new(translation.0, translation.1, translation.2),
262 scale: Vec3::splat(3.0),
263 ..default()
264 },
265 Sprite::from_atlas_image(
266 atlas_texture,
267 atlas_sources.handle(atlas_handle, vendor_handle).unwrap(),
268 ),
269 ));
270}
271
272fn create_label(
274 commands: &mut Commands,
275 translation: (f32, f32, f32),
276 text: &str,
277 text_style: TextFont,
278) {
279 commands.spawn((
280 Text2d::new(text),
281 text_style,
282 TextLayout::new_with_justify(Justify::Center),
283 Transform {
284 translation: Vec3::new(translation.0, translation.1, translation.2),
285 ..default()
286 },
287 ));
288}