feat: Functionally working stairs without sprite

- Made all observers public
- Observe all InteractionEvents in the app, not in the entity
- Added names to layout bundles
- Raised player speed to 4 m/s for quicker debugging
- Added second container on 2nd floor
This commit is contained in:
Alexey 2026-03-24 16:10:43 +03:00
commit 5157450ced
7 changed files with 129 additions and 9 deletions

98
src/layout/stairs.rs Normal file
View file

@ -0,0 +1,98 @@
use bevy::{ecs::relationship::RelatedSpawner, prelude::*};
use bevy_rapier2d::prelude::*;
use crate::{meters, player::Player};
use super::*;
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
#[reflect(Component, Debug, PartialEq, Default, Clone)]
#[require(InteractiveObject, Collider, Sensor)]
pub enum StairCollider {
#[default]
Up,
Down,
}
#[derive(Component, Clone, Copy, Default, Reflect, Debug, PartialEq)]
#[reflect(Component, Clone, Default, Debug, PartialEq)]
#[require(Transform)]
pub struct Stairs {
pub up: Option<Vec2>,
pub down: Option<Vec2>,
}
pub fn on_stairs_interact(
event: On<InteractionEvent>,
collider_query: Query<(&ChildOf, &StairCollider)>,
stairs_query: Query<&Stairs>,
mut player_query: Query<&mut Transform, With<Player>>,
) {
let Ok((parent, collider_type)) = collider_query.get(event.entity) else {
return;
};
let Ok(stairs) = stairs_query.get(parent.0) else {
error!("StairCollider does not have Stairs parent");
return;
};
let Ok(mut player_transform) = player_query.single_mut() else {
error!("Player is not single");
return;
};
let offset = match collider_type {
StairCollider::Up => {
let Some(offset) = stairs.up else {
error!("StairCollider::Up triggered but Stairs.up is None");
return;
};
offset
},
StairCollider::Down => {
let Some(offset) = stairs.down else {
error!("StairCollider::Down triggered but Stairs.down is None");
return;
};
offset
},
};
player_transform.translation.x += offset.x;
player_transform.translation.y += offset.y;
}
pub fn stairs_bundle(
_asset_server: &Res<AssetServer>,
position: Vec2,
up_position: Option<Vec2>,
down_position: Option<Vec2>,
) -> impl Bundle {
let (has_up, has_down) = (up_position.is_some(), down_position.is_some());
(
Stairs {
up: up_position,
down: down_position,
},
Transform::from_xyz(position.x, position.y, -1.),
Children::spawn(
SpawnWith(move |parent: &mut RelatedSpawner<ChildOf>| {
if has_up {
parent.spawn((
StairCollider::Up,
Collider::cuboid(meters(1.), meters(1.)),
Transform::from_xyz(meters(-1.), 0., 0.),
));
}
if has_down {
parent.spawn((
StairCollider::Down,
Collider::cuboid(meters(1.), meters(1.)),
Transform::from_xyz(meters(1.), 0., 0.),
));
}
})
),
Name::new(format!("Stairs ({},{})", position.x, position.y))
)
}