healing particle effect

This commit is contained in:
2025-05-07 17:10:31 +02:00
parent 3ce61bfe5b
commit b5ec1229b8
6 changed files with 107 additions and 1 deletions

Binary file not shown.

View File

@@ -6,7 +6,7 @@ use crate::{
}; };
#[derive(Component)] #[derive(Component)]
struct Healing(Entity); pub struct Healing(pub Entity);
#[derive(Event, Debug)] #[derive(Event, Debug)]
pub enum HealingStateChanged { pub enum HealingStateChanged {

View File

@@ -4,6 +4,8 @@ mod healing;
mod missile; mod missile;
mod thrown; mod thrown;
pub use healing::Healing;
use crate::{ use crate::{
GameState, GameState,
aim::AimTarget, aim::AimTarget,

99
src/heal_effect.rs Normal file
View File

@@ -0,0 +1,99 @@
use crate::{
GameState, abilities::Healing, loading_assets::GameAssets, utils::billboards::Billboard,
};
use bevy::prelude::*;
use rand::{Rng, thread_rng};
#[derive(Component, Default)]
#[require(Transform, InheritedVisibility)]
struct HealParticleEffect {
next_spawn: f32,
}
#[derive(Component)]
struct HealParticle {
start_scale: f32,
end_scale: f32,
start_pos: Vec3,
end_pos: Vec3,
start_time: f32,
life_time: f32,
}
pub fn plugin(app: &mut App) {
app.add_systems(
Update,
(on_added, update_effects, update_particles).run_if(in_state(GameState::Playing)),
);
}
fn on_added(mut cmds: Commands, query: Query<&Healing, Added<Healing>>) {
for healing in query.iter() {
cmds.entity(healing.0).insert((
Name::new("heal-particle-effect"),
HealParticleEffect::default(),
));
}
}
fn update_effects(
mut cmds: Commands,
mut query: Query<(&mut HealParticleEffect, Entity)>,
time: Res<Time>,
assets: Res<GameAssets>,
) {
const DISTANCE: f32 = 4.;
let mut rng = thread_rng();
let now = time.elapsed_secs();
for (mut effect, e) in query.iter_mut() {
if effect.next_spawn < now {
let start_pos = Vec3::new(
rng.gen_range(-DISTANCE..DISTANCE),
2.,
rng.gen_range(-DISTANCE..DISTANCE),
);
let max_distance = start_pos.length().max(0.8);
let end_pos =
start_pos + (start_pos.normalize() * -1.) * rng.gen_range(0.5..max_distance);
let start_scale = rng.gen_range(0.7..1.0);
let end_scale = rng.gen_range(0.1..start_scale);
cmds.entity(e).with_child((
Name::new("heal-particle"),
SceneRoot(assets.mesh_heal_particle.clone()),
Billboard,
Transform::from_translation(start_pos),
HealParticle {
start_scale,
end_scale,
start_pos,
end_pos,
start_time: now,
life_time: rng.gen_range(0.3..1.0),
},
));
effect.next_spawn = now + rng.gen_range(0.1..0.5);
}
}
}
fn update_particles(
mut cmds: Commands,
mut query: Query<(&mut Transform, &HealParticle, Entity)>,
time: Res<Time>,
) {
for (mut transform, particle, e) in query.iter_mut() {
if particle.start_time + particle.life_time < time.elapsed_secs() {
cmds.entity(e).despawn();
continue;
}
let t = (time.elapsed_secs() - particle.start_time) / particle.life_time;
// info!("particle[{e:?}] t: {t}");
transform.translation = particle.start_pos.lerp(particle.end_pos, t);
transform.scale = Vec3::splat(particle.start_scale.lerp(particle.end_scale, t));
}
}

View File

@@ -100,6 +100,9 @@ pub struct GameAssets {
#[asset(path = "models/cash.glb#Scene0")] #[asset(path = "models/cash.glb#Scene0")]
pub mesh_cash: Handle<Scene>, pub mesh_cash: Handle<Scene>,
#[asset(path = "models/medic_particle.glb#Scene0")]
pub mesh_heal_particle: Handle<Scene>,
#[asset(path = "models/projectiles", collection(mapped, typed))] #[asset(path = "models/projectiles", collection(mapped, typed))]
pub projectiles: HashMap<AssetFileName, Handle<Gltf>>, pub projectiles: HashMap<AssetFileName, Handle<Gltf>>,

View File

@@ -14,6 +14,7 @@ mod head;
mod head_drop; mod head_drop;
mod heads; mod heads;
mod heads_database; mod heads_database;
mod heal_effect;
mod hitpoints; mod hitpoints;
mod keys; mod keys;
mod loading_assets; mod loading_assets;
@@ -165,6 +166,7 @@ fn main() {
app.add_plugins(water::plugin); app.add_plugins(water::plugin);
app.add_plugins(head_drop::plugin); app.add_plugins(head_drop::plugin);
app.add_plugins(trail::plugin); app.add_plugins(trail::plugin);
app.add_plugins(heal_effect::plugin);
app.init_state::<GameState>(); app.init_state::<GameState>();