feat: Finished door mechanics

- Door now has i8 direction (-1 or 1)
- Door may be locked with Locked component
- Player entity RigidBody now cannot sleep to keep MayInteract when idle
This commit is contained in:
Alexey 2026-03-18 13:29:05 +03:00
commit bb357d8062
2 changed files with 37 additions and 16 deletions

View file

@ -17,7 +17,10 @@ pub struct Wall;
#[derive(Component)] #[derive(Component)]
#[require(Sprite, InteractiveObject)] #[require(Sprite, InteractiveObject)]
pub struct Door; pub struct Door(pub i8);
#[derive(Component)]
pub struct Locked;
#[derive(EntityEvent)] #[derive(EntityEvent)]
pub struct InteractionEvent { pub struct InteractionEvent {
@ -76,24 +79,35 @@ pub fn detect_interact_collisions(
fn on_door_interact( fn on_door_interact(
event: On<InteractionEvent>, event: On<InteractionEvent>,
mut commands: Commands, mut commands: Commands,
locked_query: Query<(), With<Locked>>,
collider_query: Query<(), (With<Door>, With<Collider>)>, collider_query: Query<(), (With<Door>, With<Collider>)>,
no_collider_query: Query<(), (With<Door>, Without<Collider>)>, no_collider_query: Query<(), (With<Door>, Without<Collider>)>,
mut sprite_query: Query<&mut Sprite, With<Door>>, door_query: Query<(&Door, &Children)>,
mut sprite_query: Query<(&mut Sprite, &mut Transform)>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
) { ) {
let was_opened = if locked_query.get(event.entity).is_ok() {
if collider_query.get(event.entity).is_ok() { false } return;
}
let was_opened = if collider_query.get(event.entity).is_ok() { false }
else if no_collider_query.get(event.entity).is_ok() { true } else if no_collider_query.get(event.entity).is_ok() { true }
else { else {
error!("on_door_interact fired but entity {} isn't door", event.entity); error!("on_door_interact fired but entity {} isn't door", event.entity);
return;
};
let Ok((door, children)) = door_query.get(event.entity) else {
error!("on_door_interact fired but entity {} has no children", event.entity);
return; return;
}; };
let Ok(mut sprite) = sprite_query.get_mut(event.entity) else { for child in children {
error!("on_door_interact fired but entity {} has no sprite", event.entity); if let Ok((mut sprite, mut transform)) = sprite_query.get_mut(*child) {
return; let (image, translation) = if was_opened { (DOOR_CLOSED_ASSET, 0.) }
}; else { (DOOR_OPENED_ASSET, door.0 as f32 * 8.) };
let asset_path = if was_opened { DOOR_CLOSED_ASSET } else { DOOR_OPENED_ASSET }; sprite.image = asset_server.load(image);
sprite.image = asset_server.load(asset_path); transform.translation.x = translation;
break;
}
}
if was_opened { if was_opened {
commands.entity(event.entity).insert(door_collider()); commands.entity(event.entity).insert(door_collider());
@ -103,13 +117,13 @@ fn on_door_interact(
} }
fn door_collider() -> Collider { fn door_collider() -> Collider {
Collider::cuboid(PIXELS_PER_METER * 0.125, PIXELS_PER_METER) Collider::cuboid(PIXELS_PER_METER * 0.06125, PIXELS_PER_METER)
} }
fn door_bundle(image: Handle<Image>, position: Vec2) -> impl Bundle { fn door_bundle(image: Handle<Image>, position: Vec2, facing_left: bool) -> impl Bundle {
let direction = if facing_left { -1 } else { 1 };
( (
Door, Door(direction),
Sprite::from_image(image),
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)),
door_collider(), door_collider(),
@ -120,6 +134,10 @@ fn door_bundle(image: Handle<Image>, position: Vec2) -> impl Bundle {
Sensor, Sensor,
Transform::from_xyz(0., 0., 0.), Transform::from_xyz(0., 0., 0.),
), ),
(
Sprite::from_image(image),
Transform::from_xyz(0., 0., 0.),
),
], ],
) )
} }
@ -138,6 +156,8 @@ pub fn setup_world(
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
) { ) {
let door_image = asset_server.load(DOOR_CLOSED_ASSET); let door_image = asset_server.load(DOOR_CLOSED_ASSET);
commands.spawn(door_bundle(door_image, vec2(128., 0.))); commands.spawn(door_bundle(door_image.clone(), vec2(128., 0.), true));
commands.spawn(door_bundle(door_image.clone(), vec2(160., 0.), false));
commands.spawn(door_bundle(door_image.clone(), vec2(196., 0.), false)).insert(Locked);
commands.spawn(wall_bundle(vec2(-128., 0.))); commands.spawn(wall_bundle(vec2(-128., 0.)));
} }

View file

@ -25,6 +25,7 @@ fn player_bundle(asset_server: &Res<AssetServer>) -> impl Bundle {
ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC, ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC,
Collider::cuboid(PIXELS_PER_METER * 0.5, PIXELS_PER_METER), Collider::cuboid(PIXELS_PER_METER * 0.5, PIXELS_PER_METER),
ActiveEvents::COLLISION_EVENTS, ActiveEvents::COLLISION_EVENTS,
Sleeping::disabled(),
children![ children![
(Inventory::new(UVec2::new(6, 2)), ActiveInventory), (Inventory::new(UVec2::new(6, 2)), ActiveInventory),
(Inventory::new(UVec2::new(5, 3)), ActiveInventory), (Inventory::new(UVec2::new(5, 3)), ActiveInventory),