use different head drop meshes

This commit is contained in:
2025-06-06 10:15:45 +02:00
parent 67320b3be4
commit 6e8e995da5
4 changed files with 66 additions and 38 deletions

Binary file not shown.

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
GameState, billboards::Billboard, global_observer, loading_assets::GameAssets, GameState, billboards::Billboard, global_observer, heads_database::HeadsDatabase,
physics_layers::GameLayer, player::Player, sounds::PlaySound, loading_assets::HeadDropAssets, physics_layers::GameLayer, player::Player, sounds::PlaySound,
squish_animation::SquishAnimation, tb_entities::SecretHead, squish_animation::SquishAnimation, tb_entities::SecretHead,
}; };
use avian3d::prelude::*; use avian3d::prelude::*;
@@ -8,7 +8,29 @@ use bevy::prelude::*;
use std::f32::consts::PI; use std::f32::consts::PI;
#[derive(Event, Reflect)] #[derive(Event, Reflect)]
pub struct HeadDrops(pub Vec3, pub usize); pub struct HeadDrops {
pos: Vec3,
head_id: usize,
impulse: bool,
}
impl HeadDrops {
pub fn new(pos: Vec3, head_id: usize) -> Self {
Self {
pos,
head_id,
impulse: true,
}
}
fn new_static(pos: Vec3, head_id: usize) -> Self {
Self {
pos,
head_id,
impulse: false,
}
}
}
#[derive(Component, Reflect)] #[derive(Component, Reflect)]
#[reflect(Component)] #[reflect(Component)]
@@ -28,63 +50,65 @@ pub fn plugin(app: &mut App) {
global_observer!(app, on_head_drop); global_observer!(app, on_head_drop);
} }
fn spawn( fn spawn(mut commands: Commands, query: Query<(Entity, &GlobalTransform, &SecretHead)>) {
mut commands: Commands,
query: Query<(Entity, &GlobalTransform, &SecretHead)>,
assets: Res<GameAssets>,
) {
for (e, t, head) in query { for (e, t, head) in query {
commands commands.trigger(HeadDrops::new_static(
.spawn(( t.translation() + Vec3::new(0., 2., 0.),
Name::new("headdrop"), head.head_id,
SecretHeadMarker, ));
HeadDrop(head.head_id),
Transform::from_translation(t.translation() + Vec3::new(0., 2., 0.)),
Visibility::default(),
Collider::sphere(1.5),
LockedAxes::ROTATION_LOCKED,
RigidBody::Dynamic,
CollisionLayers::new(LayerMask(GameLayer::Collectibles.to_bits()), LayerMask::ALL),
CollisionEventsEnabled,
Restitution::new(0.6),
))
.with_child((
Billboard,
SquishAnimation(2.6),
SceneRoot(assets.mesh_head_drop.clone()),
));
commands.entity(e).despawn(); commands.entity(e).despawn();
} }
} }
fn on_head_drop(trigger: Trigger<HeadDrops>, mut commands: Commands, assets: Res<GameAssets>) { fn on_head_drop(
let HeadDrops(position, id) = trigger.event(); trigger: Trigger<HeadDrops>,
mut commands: Commands,
assets: Res<HeadDropAssets>,
heads_db: Res<HeadsDatabase>,
gltf_assets: Res<Assets<Gltf>>,
) -> Result<(), BevyError> {
let drop = trigger.event();
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();
commands.trigger(PlaySound::HeadDrop); if drop.impulse {
commands.trigger(PlaySound::HeadDrop);
}
let ability = format!("{:?}.glb", heads_db.head_stats(drop.head_id).ability).to_lowercase();
let mesh = if let Some(handle) = assets.meshes.get(ability.as_str()) {
gltf_assets.get(handle)
} else {
gltf_assets.get(&assets.meshes["none.glb"])
}
.ok_or_else(|| "asset not found")?;
commands commands
.spawn(( .spawn((
Name::new("headdrop"), Name::new("headdrop"),
HeadDrop(*id), HeadDrop(drop.head_id),
Transform::from_translation(*position), Transform::from_translation(drop.pos),
Visibility::default(), Visibility::default(),
Collider::sphere(1.5), Collider::sphere(1.5),
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
LockedAxes::ROTATION_LOCKED, LockedAxes::ROTATION_LOCKED,
RigidBody::Dynamic, RigidBody::Dynamic,
CollisionLayers::new(LayerMask(GameLayer::Collectibles.to_bits()), LayerMask::ALL), CollisionLayers::new(LayerMask(GameLayer::Collectibles.to_bits()), LayerMask::ALL),
CollisionEventsEnabled, CollisionEventsEnabled,
Restitution::new(0.6), Restitution::new(0.6),
)) ))
.insert_if(
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
|| drop.impulse,
)
.with_child(( .with_child((
Billboard, Billboard,
SquishAnimation(2.6), SquishAnimation(2.6),
SceneRoot(assets.mesh_head_drop.clone()), SceneRoot(mesh.scenes[0].clone()),
)); ));
Ok(())
} }
fn collect_head( fn collect_head(

View File

@@ -67,6 +67,12 @@ struct HeadsAssets {
heads: Handle<HeadDatabaseAsset>, heads: Handle<HeadDatabaseAsset>,
} }
#[derive(AssetCollection, Resource)]
pub struct HeadDropAssets {
#[asset(path = "models/head_drops", collection(mapped, typed))]
pub meshes: HashMap<AssetFileName, Handle<Gltf>>,
}
#[derive(AssetCollection, Resource)] #[derive(AssetCollection, Resource)]
pub struct UIAssets { pub struct UIAssets {
#[asset(path = "font.ttf")] #[asset(path = "font.ttf")]
@@ -93,9 +99,6 @@ pub struct GameAssets {
#[asset(path = "models/key.glb#Scene0")] #[asset(path = "models/key.glb#Scene0")]
pub mesh_key: Handle<Scene>, pub mesh_key: Handle<Scene>,
#[asset(path = "models/head_drop.glb#Scene0")]
pub mesh_head_drop: Handle<Scene>,
#[asset(path = "models/spawn.glb#Scene0")] #[asset(path = "models/spawn.glb#Scene0")]
pub mesh_spawn: Handle<Scene>, pub mesh_spawn: Handle<Scene>,
@@ -122,6 +125,7 @@ impl Plugin for LoadingPlugin {
.load_collection::<AudioAssets>() .load_collection::<AudioAssets>()
.load_collection::<GameAssets>() .load_collection::<GameAssets>()
.load_collection::<HeadsAssets>() .load_collection::<HeadsAssets>()
.load_collection::<HeadDropAssets>()
.load_collection::<UIAssets>(), .load_collection::<UIAssets>(),
); );
} }

View File

@@ -59,7 +59,7 @@ fn on_kill(
return; return;
}; };
commands.trigger(HeadDrops(transform.translation, head.0)); commands.trigger(HeadDrops::new(transform.translation, head.0));
commands.entity(trigger.target()).despawn(); commands.entity(trigger.target()).despawn();