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)]
#[require(Sprite, InteractiveObject)]
pub struct Door;
pub struct Door(pub i8);
#[derive(Component)]
pub struct Locked;
#[derive(EntityEvent)]
pub struct InteractionEvent {
@ -76,24 +79,35 @@ pub fn detect_interact_collisions(
fn on_door_interact(
event: On<InteractionEvent>,
mut commands: Commands,
locked_query: Query<(), With<Locked>>,
collider_query: Query<(), (With<Door>, With<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>,
) {
let was_opened =
if collider_query.get(event.entity).is_ok() { false }
if locked_query.get(event.entity).is_ok() {
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 {
error!("on_door_interact fired but entity {} isn't door", event.entity);
return;
};
let Ok(mut sprite) = sprite_query.get_mut(event.entity) else {
error!("on_door_interact fired but entity {} has no sprite", event.entity);
let Ok((door, children)) = door_query.get(event.entity) else {
error!("on_door_interact fired but entity {} has no children", event.entity);
return;
};
let asset_path = if was_opened { DOOR_CLOSED_ASSET } else { DOOR_OPENED_ASSET };
sprite.image = asset_server.load(asset_path);
for child in children {
if let Ok((mut sprite, mut transform)) = sprite_query.get_mut(*child) {
let (image, translation) = if was_opened { (DOOR_CLOSED_ASSET, 0.) }
else { (DOOR_OPENED_ASSET, door.0 as f32 * 8.) };
sprite.image = asset_server.load(image);
transform.translation.x = translation;
break;
}
}
if was_opened {
commands.entity(event.entity).insert(door_collider());
@ -103,13 +117,13 @@ fn on_door_interact(
}
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,
Sprite::from_image(image),
Door(direction),
Transform::from_xyz(position.x, position.y, 0.),
Name::new(format!("Door ({}, {})", position.x, position.y)),
door_collider(),
@ -120,6 +134,10 @@ fn door_bundle(image: Handle<Image>, position: Vec2) -> impl Bundle {
Sensor,
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>,
) {
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.)));
}

View file

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