use observable collision events

to simplify key/head_drop collecting
This commit is contained in:
2025-06-20 00:39:33 +02:00
parent 71d43767bd
commit 99eb0bf9c8
2 changed files with 54 additions and 51 deletions

View File

@@ -4,7 +4,10 @@ use crate::{
squish_animation::SquishAnimation, tb_entities::SecretHead, squish_animation::SquishAnimation, tb_entities::SecretHead,
}; };
use avian3d::prelude::*; use avian3d::prelude::*;
use bevy::prelude::*; use bevy::{
ecs::{relationship::RelatedSpawner, spawn::SpawnWith},
prelude::*,
};
use std::f32::consts::PI; use std::f32::consts::PI;
#[derive(Event, Reflect)] #[derive(Event, Reflect)]
@@ -44,7 +47,6 @@ struct SecretHeadMarker;
pub struct HeadCollected(pub usize); pub struct HeadCollected(pub usize);
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.add_systems(Update, collect_head.run_if(in_state(GameState::Playing)));
app.add_systems(OnEnter(GameState::Playing), spawn); app.add_systems(OnEnter(GameState::Playing), spawn);
global_observer!(app, on_head_drop); global_observer!(app, on_head_drop);
@@ -85,6 +87,8 @@ fn on_head_drop(
} }
.ok_or("asset not found")?; .ok_or("asset not found")?;
let head_id = drop.head_id;
commands commands
.spawn(( .spawn((
Name::new("headdrop"), Name::new("headdrop"),
@@ -97,15 +101,18 @@ fn on_head_drop(
GameLayer::CollectiblePhysics, GameLayer::CollectiblePhysics,
LayerMask::ALL & !GameLayer::Player.to_bits(), LayerMask::ALL & !GameLayer::Player.to_bits(),
), ),
CollisionEventsEnabled,
Restitution::new(0.6), Restitution::new(0.6),
children![( Children::spawn(SpawnWith(move |parent: &mut RelatedSpawner<ChildOf>| {
parent
.spawn((
Collider::sphere(1.5), Collider::sphere(1.5),
CollisionLayers::new(GameLayer::CollectibleSensors, GameLayer::Player), CollisionLayers::new(GameLayer::CollectibleSensors, GameLayer::Player),
Sensor, Sensor,
CollisionEventsEnabled, CollisionEventsEnabled,
HeadDrop(drop.head_id), HeadDrop(head_id),
)], ))
.observe(on_collect_head);
})),
)) ))
.insert_if( .insert_if(
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false), ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
@@ -120,23 +127,18 @@ fn on_head_drop(
Ok(()) Ok(())
} }
fn collect_head( fn on_collect_head(
trigger: Trigger<OnCollisionStart>,
mut commands: Commands, mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
query_player: Query<&Player>, query_player: Query<&Player>,
query_collectable: Query<(&HeadDrop, &ChildOf)>, query_collectable: Query<(&HeadDrop, &ChildOf)>,
query_secret: Query<&SecretHeadMarker>, query_secret: Query<&SecretHeadMarker>,
) { ) {
for CollisionStarted(e1, e2) in collision_event_reader.read() { let collectable = trigger.target();
let collectable = if query_player.contains(*e1) && query_collectable.contains(*e2) { let collider = trigger.collider;
*e2
} else if query_player.contains(*e2) && query_collectable.contains(*e1) {
*e1
} else {
continue;
};
let (key, child_of) = query_collectable.get(collectable).unwrap(); if query_player.contains(collider) {
let (drop, child_of) = query_collectable.get(collectable).unwrap();
let is_secret = query_secret.contains(collectable); let is_secret = query_secret.contains(collectable);
@@ -145,7 +147,8 @@ fn collect_head(
} else { } else {
commands.trigger(PlaySound::HeadCollect); commands.trigger(PlaySound::HeadCollect);
} }
commands.trigger(HeadCollected(key.0));
commands.trigger(HeadCollected(drop.0));
commands.entity(child_of.parent()).despawn(); commands.entity(child_of.parent()).despawn();
} }
} }

View File

@@ -1,10 +1,12 @@
use crate::{ use crate::{
GameState, billboards::Billboard, global_observer, loading_assets::GameAssets, billboards::Billboard, global_observer, loading_assets::GameAssets, physics_layers::GameLayer,
physics_layers::GameLayer, player::Player, sounds::PlaySound, player::Player, sounds::PlaySound, squish_animation::SquishAnimation,
squish_animation::SquishAnimation,
}; };
use avian3d::prelude::*; use avian3d::prelude::*;
use bevy::prelude::*; use bevy::{
ecs::{relationship::RelatedSpawner, spawn::SpawnWith},
prelude::*,
};
use std::f32::consts::PI; use std::f32::consts::PI;
#[derive(Event, Reflect)] #[derive(Event, Reflect)]
@@ -18,14 +20,14 @@ struct Key(pub String);
pub struct KeyCollected(pub String); pub struct KeyCollected(pub String);
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.add_systems(Update, collect_key.run_if(in_state(GameState::Playing)));
global_observer!(app, on_spawn_key); global_observer!(app, on_spawn_key);
} }
fn on_spawn_key(trigger: Trigger<KeySpawn>, mut commands: Commands, assets: Res<GameAssets>) { fn on_spawn_key(trigger: Trigger<KeySpawn>, mut commands: Commands, assets: Res<GameAssets>) {
let KeySpawn(position, id) = trigger.event(); let KeySpawn(position, id) = trigger.event();
let id = id.clone();
let angle = rand::random::<f32>() * PI * 2.; let angle = rand::random::<f32>() * PI * 2.;
let spawn_dir = Quat::from_rotation_y(angle) * Vec3::new(0.5, 0.6, 0.).normalize(); let spawn_dir = Quat::from_rotation_y(angle) * Vec3::new(0.5, 0.6, 0.).normalize();
@@ -38,41 +40,39 @@ fn on_spawn_key(trigger: Trigger<KeySpawn>, mut commands: Commands, assets: Res<
LockedAxes::ROTATION_LOCKED, LockedAxes::ROTATION_LOCKED,
RigidBody::Dynamic, RigidBody::Dynamic,
CollisionLayers::new(GameLayer::CollectiblePhysics, GameLayer::Level), CollisionLayers::new(GameLayer::CollectiblePhysics, GameLayer::Level),
CollisionEventsEnabled,
Restitution::new(0.6), Restitution::new(0.6),
children![ Children::spawn((
( Spawn((
Billboard, Billboard,
SquishAnimation(2.6), SquishAnimation(2.6),
SceneRoot(assets.mesh_key.clone()), SceneRoot(assets.mesh_key.clone()),
), )),
( SpawnWith(|parent: &mut RelatedSpawner<ChildOf>| {
parent
.spawn((
Collider::sphere(1.5), Collider::sphere(1.5),
CollisionLayers::new(GameLayer::CollectibleSensors, GameLayer::Player), CollisionLayers::new(GameLayer::CollectibleSensors, GameLayer::Player),
Sensor, Sensor,
CollisionEventsEnabled, CollisionEventsEnabled,
Key(id.clone()), Key(id),
) ))
], .observe(on_collect_key);
}),
)),
)); ));
} }
fn collect_key( fn on_collect_key(
trigger: Trigger<OnCollisionStart>,
mut commands: Commands, mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
query_player: Query<&Player>, query_player: Query<&Player>,
query_collectable: Query<(&Key, &ChildOf)>, query_collectable: Query<(&Key, &ChildOf)>,
) { ) {
for CollisionStarted(e1, e2) in collision_event_reader.read() { let key = trigger.target();
let collectable = if query_player.contains(*e1) && query_collectable.contains(*e2) { let collider = trigger.collider;
*e2
} else if query_player.contains(*e2) && query_collectable.contains(*e1) {
*e1
} else {
continue;
};
let (key, child_of) = query_collectable.get(collectable).unwrap(); if query_player.contains(collider) {
let (key, child_of) = query_collectable.get(key).unwrap();
commands.trigger(PlaySound::KeyCollect); commands.trigger(PlaySound::KeyCollect);
commands.trigger(KeyCollected(key.0.clone())); commands.trigger(KeyCollected(key.0.clone()));