feat: Implemented layout assets resource

- Layout sprites are now looking as before
This commit is contained in:
Alexey 2026-04-01 11:50:56 +03:00
commit c1f9c29fe7
9 changed files with 131 additions and 72 deletions

View file

@ -57,14 +57,14 @@ pub fn load_level (
let player_pos = vec2(meters(level.interactive.player.x + 0.5), meters(level.interactive.player.y)); let player_pos = vec2(meters(level.interactive.player.x + 0.5), meters(level.interactive.player.y));
commands.spawn(Level).with_children(|parent| { commands.spawn(Level).with_children(|parent| {
parent.spawn(tilemap_bundle(&asset_server, tiles)); parent.spawn(tilemap_bundle(&textures, tiles));
parent.spawn(player_bundle(&asset_server, player_pos)); parent.spawn(player_bundle(&asset_server, player_pos));
for DoorData { pos, facing_left, lock } in level.interactive.doors.iter() { for DoorData { pos, facing_left, lock } in level.interactive.doors.iter() {
let door_pos = vec2(meters(pos.x + 0.5), meters(pos.y)); let door_pos = vec2(meters(pos.x + 0.5), meters(pos.y));
let mut door = parent.spawn(door_bundle(&textures, door_pos, *facing_left)); let mut door = parent.spawn(door_bundle(&textures, door_pos, *facing_left));
if let Some(lock_facing_left) = lock { if let Some(lock_facing_left) = lock {
door.with_child(padlock_bundle(&asset_server, *lock_facing_left)); door.with_child(padlock_bundle(&textures, *lock_facing_left));
} }
} }
@ -78,7 +78,7 @@ pub fn load_level (
if i == *floors - 1 { if i == *floors - 1 {
up = None; up = None;
} }
parent.spawn(stairs_bundle(&asset_server, pos, up, down)); parent.spawn(stairs_bundle(&textures, pos, up, down));
pos.y += meters(4.); pos.y += meters(4.);
down = Some(vec2(meters(-2.), meters(-4.))); down = Some(vec2(meters(-2.), meters(-4.)));
} }
@ -100,7 +100,7 @@ pub fn load_level (
for LampData { pos, intensity, radius } in level.interactive.lamps.iter() { for LampData { pos, intensity, radius } in level.interactive.lamps.iter() {
let pos = vec2(meters(pos.x), meters(pos.y - 0.5)); let pos = vec2(meters(pos.x), meters(pos.y - 0.5));
parent.spawn(lamp_bundle(&asset_server, pos, *intensity, meters(*radius))); parent.spawn(lamp_bundle(&textures, pos, *intensity, meters(*radius)));
} }
}); });
} }

View file

@ -7,7 +7,7 @@ use crate::{
inventory::{ inventory::{
ActiveInventory, ActiveInventory,
Inventory, Inventory,
} },
}; };
use super::*; use super::*;
@ -40,18 +40,8 @@ pub fn container_bundle(
position: Vec2, position: Vec2,
inventory_size: UVec2, inventory_size: UVec2,
) -> impl Bundle { ) -> impl Bundle {
let texture_atlas = TextureAtlas { let sprite = textures.container.sprite("main");
layout: textures.container.atlas.clone(), let highlight_sprite = textures.container.sprite("highlight");
index: textures.container.indices["main"],
};
let hl_texture_atlas = texture_atlas.clone().with_index(textures.container.indices["highlighted"]);
let sprite = Sprite {
image: textures.container.image.clone(),
texture_atlas: Some(texture_atlas),
..default()
};
let mut highlight_sprite = sprite.clone();
highlight_sprite.texture_atlas = Some(hl_texture_atlas);
( (
Container, Container,
Transform::from_xyz(position.x, position.y - meters(0.5), 0.), Transform::from_xyz(position.x, position.y - meters(0.5), 0.),

View file

@ -83,20 +83,15 @@ pub fn door_collider_bundle() -> impl Bundle {
) )
} }
fn door_sprites(texture: &AtlasLayoutTexture, facing_left: bool) -> (Sprite, Sprite) { fn door_sprites(texture: &AtlasLayoutTexture, flip_x: bool) -> (Sprite, Sprite) {
let texture_atlas = TextureAtlas {
layout: texture.atlas.clone(),
index: texture.indices["main"],
};
let hl_texture_atlas = texture_atlas.clone().with_index(texture.indices["highlighted"]);
let sprite = Sprite { let sprite = Sprite {
image: texture.image.clone(), flip_x,
flip_x: facing_left, ..texture.sprite("main")
texture_atlas: Some(texture_atlas), };
..default() let highlight_sprite = Sprite {
flip_x,
..texture.sprite("highlight")
}; };
let mut highlight_sprite = sprite.clone();
highlight_sprite.texture_atlas = Some(hl_texture_atlas);
(sprite, highlight_sprite) (sprite, highlight_sprite)
} }
@ -107,6 +102,7 @@ pub fn door_bundle(textures: &Res<LayoutTextures>, position: Vec2, facing_left:
Door(direction), Door(direction),
Transform::from_xyz(position.x, position.y, 0.), Transform::from_xyz(position.x, position.y, 0.),
Name::new(format!("Door ({}, {})", position.x, position.y)), Name::new(format!("Door ({}, {})", position.x, position.y)),
InheritedVisibility::VISIBLE,
children![ children![
door_collider_bundle(), door_collider_bundle(),
( (

View file

@ -1,17 +1,14 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_light_2d::prelude::*; use bevy_light_2d::prelude::*;
use crate::meters; use crate::{layout::LayoutTextures, meters};
const LAMP_IMAGE_PATH: &'static str = "sprites/level/lamp.png";
#[derive(Component, Clone, Copy, Default, Reflect, Debug, PartialEq, Eq)] #[derive(Component, Clone, Copy, Default, Reflect, Debug, PartialEq, Eq)]
#[reflect(Component, Clone, Default, Debug, PartialEq)] #[reflect(Component, Clone, Default, Debug, PartialEq)]
#[require(Transform)] #[require(Transform)]
pub struct Lamp; pub struct Lamp;
pub fn lamp_bundle(asset_server: &Res<AssetServer>, pos: Vec2, intensity: f32, radius: f32) -> impl Bundle { pub fn lamp_bundle(textures: &Res<LayoutTextures>, pos: Vec2, intensity: f32, radius: f32) -> impl Bundle {
let image = asset_server.load(LAMP_IMAGE_PATH);
( (
SpotLight2d { SpotLight2d {
intensity, intensity,
@ -21,6 +18,11 @@ pub fn lamp_bundle(asset_server: &Res<AssetServer>, pos: Vec2, intensity: f32, r
..default() ..default()
}, },
Transform::from_xyz(pos.x, pos.y, 0.), Transform::from_xyz(pos.x, pos.y, 0.),
Sprite::from_image(image), Sprite::from_image(textures.light.0.clone()),
children![
(
)
],
) )
} }

View file

@ -10,8 +10,6 @@ use crate::{
use super::*; use super::*;
const PADLOCK_IMAGE_PATH: &'static str = "sprites/interactive/padlock.png";
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)] #[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
#[reflect(Component, Debug, PartialEq, Default, Clone)] #[reflect(Component, Debug, PartialEq, Default, Clone)]
#[require(InteractiveObject)] #[require(InteractiveObject)]
@ -51,21 +49,29 @@ pub fn on_padlock_interaction(
commands.entity(lockpick_id).despawn(); commands.entity(lockpick_id).despawn();
} }
pub fn padlock_bundle(asset_server: &Res<AssetServer>, facing_left: bool) -> impl Bundle { pub fn padlock_bundle(textures: &Res<LayoutTextures>, facing_left: bool) -> impl Bundle {
let image = asset_server.load(PADLOCK_IMAGE_PATH);
let sign = if facing_left { -1. } else { 1. }; let sign = if facing_left { -1. } else { 1. };
( (
Padlock, Padlock,
Sprite { Sprite {
image, flip_x: !facing_left,
flip_x: facing_left, ..textures.lock.sprite("main")
..default()
}, },
Transform::from_xyz(meters(sign * 0.125), meters(0.), 0.), Transform::from_xyz(meters(sign * 0.125), meters(0.), 0.),
Children::spawn_one(( InheritedVisibility::VISIBLE,
Children::spawn((
Spawn((
Transform::from_xyz(meters(sign * 0.1875), 0., 0.), Transform::from_xyz(meters(sign * 0.1875), 0., 0.),
Collider::cuboid(meters(0.1875), meters(1.)), Collider::cuboid(meters(0.1875), meters(1.)),
Sensor, Sensor,
)), )),
Spawn((
Sprite {
flip_x: !facing_left,
..textures.lock.sprite("highlight")
},
Visibility::Hidden,
)),
)),
) )
} }

View file

@ -59,6 +59,21 @@ impl AtlasLayoutTexture {
) -> Self { ) -> Self {
Self { image, atlas, indices } Self { image, atlas, indices }
} }
pub fn texture_atlas(&self, name: &str) -> TextureAtlas {
TextureAtlas {
layout: self.atlas.clone(),
index: self.indices[name],
}
}
pub fn sprite(&self, name: &str) -> Sprite {
Sprite {
image: self.image.clone(),
texture_atlas: Some(self.texture_atlas(name)),
..default()
}
}
} }
#[derive(Resource, Reflect, Clone, PartialEq, Eq, Debug, Default)] #[derive(Resource, Reflect, Clone, PartialEq, Eq, Debug, Default)]

View file

@ -5,8 +5,6 @@ use crate::{meters, player::Player};
use super::*; use super::*;
const STAIRS_SPRITE_PATH: &'static str = "sprites/interactive/stairs.png";
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)] #[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
#[reflect(Component, Debug, PartialEq, Default, Clone)] #[reflect(Component, Debug, PartialEq, Default, Clone)]
#[require(InteractiveObject, Collider, Sensor)] #[require(InteractiveObject, Collider, Sensor)]
@ -63,13 +61,13 @@ pub fn on_stairs_interact(
} }
pub fn stairs_bundle( pub fn stairs_bundle(
asset_server: &Res<AssetServer>, textures: &Res<LayoutTextures>,
position: Vec2, position: Vec2,
up_position: Option<Vec2>, up_position: Option<Vec2>,
down_position: Option<Vec2>, down_position: Option<Vec2>,
) -> impl Bundle { ) -> impl Bundle {
let image = asset_server.load(STAIRS_SPRITE_PATH);
let (has_up, has_down) = (up_position.is_some(), down_position.is_some()); let (has_up, has_down) = (up_position.is_some(), down_position.is_some());
let stairs = textures.stairs.clone();
( (
Stairs { Stairs {
up: up_position, up: up_position,
@ -84,9 +82,16 @@ pub fn stairs_bundle(
StairCollider::Up, StairCollider::Up,
Collider::cuboid(meters(1.), meters(1.)), Collider::cuboid(meters(1.), meters(1.)),
Transform::from_xyz(meters(-1.), 0., 0.), Transform::from_xyz(meters(-1.), 0., 0.),
InheritedVisibility::VISIBLE,
Children::spawn(
Spawn((
stairs.sprite("up"),
Visibility::Hidden,
)),
),
)); ));
parent.spawn(( parent.spawn((
Sprite::from_image(image), stairs.sprite("main"),
Transform::from_xyz(0., meters(1.5), 0.), Transform::from_xyz(0., meters(1.5), 0.),
)); ));
} }
@ -96,6 +101,13 @@ pub fn stairs_bundle(
StairCollider::Down, StairCollider::Down,
Collider::cuboid(meters(1.), meters(1.)), Collider::cuboid(meters(1.), meters(1.)),
Transform::from_xyz(meters(1.), 0., 0.), Transform::from_xyz(meters(1.), 0., 0.),
InheritedVisibility::VISIBLE,
Children::spawn(
Spawn((
stairs.sprite("down"),
Visibility::Hidden,
)),
),
)); ));
} }
}), }),

View file

@ -1,6 +1,5 @@
use bevy::{ use bevy::{
ecs::query::QueryFilter, ecs::query::QueryFilter, image::{ImageArrayLayout, ImageLoaderSettings}, prelude::*
prelude::*,
}; };
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
@ -102,14 +101,14 @@ fn double_atlas(
"main".to_owned(), "main".to_owned(),
atlas.add_texture(URect::from_corners( atlas.add_texture(URect::from_corners(
uvec2(0, 0), uvec2(0, 0),
uvec2(single_size.x, single_size.y) uvec2(single_size.x, single_size.y),
)), )),
); );
indices.insert( indices.insert(
"highlighted".to_owned(), "highlight".to_owned(),
atlas.add_texture(URect::from_corners( atlas.add_texture(URect::from_corners(
uvec2(single_size.x, 0), uvec2(single_size.x, 0),
uvec2(single_size.x * 2, single_size.y) uvec2(single_size.x * 2, single_size.y),
)), )),
); );
atlases.add(atlas) atlases.add(atlas)
@ -122,19 +121,68 @@ pub fn load_layout_textures(
) { ) {
// Container // Container
let image = asset_server.load("sprites/interactive/container.png"); let image = asset_server.load("sprites/interactive/container.png");
let mut indices = HashMap::new(); let mut indices = HashMap::with_capacity(2);
let atlas = double_atlas(&mut atlases, &mut indices, uvec2(32, 16)); let atlas = double_atlas(&mut atlases, &mut indices, uvec2(32, 16));
textures.container = AtlasLayoutTexture::new(image, atlas, indices); textures.container = AtlasLayoutTexture::new(image, atlas, indices);
// Closed door // Closed door
let image = asset_server.load("sprites/interactive/door_closed.png"); let image = asset_server.load("sprites/interactive/door_closed.png");
let mut indices = HashMap::new(); let mut indices = HashMap::with_capacity(2);
let atlas = double_atlas(&mut atlases, &mut indices, uvec2(4, 32)); let atlas = double_atlas(&mut atlases, &mut indices, uvec2(4, 32));
textures.door_closed = AtlasLayoutTexture::new(image, atlas, indices); textures.door_closed = AtlasLayoutTexture::new(image, atlas, indices);
// Opened door // Opened door
let image = asset_server.load("sprites/interactive/door_opened.png"); let image = asset_server.load("sprites/interactive/door_opened.png");
let mut indices = HashMap::new(); let mut indices = HashMap::with_capacity(2);
let atlas = double_atlas(&mut atlases, &mut indices, uvec2(16, 32)); let atlas = double_atlas(&mut atlases, &mut indices, uvec2(16, 32));
textures.door_opened = AtlasLayoutTexture::new(image, atlas, indices); textures.door_opened = AtlasLayoutTexture::new(image, atlas, indices);
// Light
let image = asset_server.load("sprites/level/lamp.png");
textures.light = SingleLayoutTexture(image);
// Padlock
let image = asset_server.load("sprites/interactive/padlock.png");
let mut indices = HashMap::with_capacity(2);
let atlas = double_atlas(&mut atlases, &mut indices, uvec2(2, 4));
textures.lock = AtlasLayoutTexture::new(image, atlas, indices);
// Stairs
let image = asset_server.load("sprites/interactive/stairs.png");
let mut indices = HashMap::with_capacity(3);
let atlas = {
let mut atlas = TextureAtlasLayout::new_empty(uvec2(72, 80));
indices.insert(
"main".to_owned(),
atlas.add_texture(URect::from_corners(
uvec2(0, 0),
uvec2(64, 80),
)),
);
indices.insert(
"up".to_owned(),
atlas.add_texture(URect::from_corners(
uvec2(64, 0),
uvec2(72, 8),
)),
);
indices.insert(
"down".to_owned(),
atlas.add_texture(URect::from_corners(
uvec2(64, 8),
uvec2(72, 16),
)),
);
atlases.add(atlas)
};
textures.stairs = AtlasLayoutTexture::new(image, atlas, indices);
// Tilemap
let image = asset_server.load_with_settings(
"sprites/level/tilemap.png",
|settings: &mut ImageLoaderSettings| {
settings.array_layout = Some(ImageArrayLayout::RowCount { rows: 3 })
}
);
textures.tilemap = SingleLayoutTexture(image);
} }

View file

@ -1,8 +1,4 @@
use bevy::{ use bevy::{
image::{
ImageArrayLayout,
ImageLoaderSettings,
},
prelude::*, prelude::*,
sprite_render::{ sprite_render::{
TileData, TileData,
@ -13,9 +9,8 @@ use bevy::{
use bevy_light_2d::prelude::*; use bevy_light_2d::prelude::*;
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
use crate::meters; use crate::{layout::LayoutTextures, meters};
const TILEMAP_PATH: &'static str = "sprites/level/tilemap.png";
const TILE_DISPLAY_SIZE: UVec2 = UVec2::splat(meters(1.) as u32); const TILE_DISPLAY_SIZE: UVec2 = UVec2::splat(meters(1.) as u32);
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)] #[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
@ -23,7 +18,7 @@ const TILE_DISPLAY_SIZE: UVec2 = UVec2::splat(meters(1.) as u32);
pub struct Tilemap; pub struct Tilemap;
pub fn tilemap_bundle( pub fn tilemap_bundle(
asset_server: &Res<AssetServer>, textures: &Res<LayoutTextures>,
tiles: Vec<(u16, URect)>, tiles: Vec<(u16, URect)>,
) -> impl Bundle { ) -> impl Bundle {
let mut size = uvec2(0, 0); let mut size = uvec2(0, 0);
@ -41,12 +36,7 @@ pub fn tilemap_bundle(
TilemapChunk { TilemapChunk {
chunk_size: size, chunk_size: size,
tile_display_size: TILE_DISPLAY_SIZE, tile_display_size: TILE_DISPLAY_SIZE,
tileset: asset_server.load_with_settings( tileset: textures.tilemap.0.clone(),
TILEMAP_PATH,
|settings: &mut ImageLoaderSettings| {
settings.array_layout = Some(ImageArrayLayout::RowCount { rows: 3 })
},
),
..default() ..default()
}, },
Transform::from_xyz( Transform::from_xyz(