Skip to content

Commit 9e006a5

Browse files
committed
add more animations to attacks
1 parent 8bb9a6c commit 9e006a5

File tree

6 files changed

+287
-16
lines changed

6 files changed

+287
-16
lines changed

assets/particles/star_06.png

34.6 KB
Loading

src/battle.rs

Lines changed: 151 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ use bevy::{
77
prelude::*,
88
render::{camera::ScalingMode, view::RenderLayers},
99
};
10+
use bevy_tweening::{
11+
lens::TransformPositionLens, Animator, Delay, EaseFunction, Tween, TweeningType,
12+
};
1013
use iyes_loopless::prelude::*;
1114
use strum::{EnumCount, IntoEnumIterator};
1215
use strum_macros::{Display, EnumCount, EnumIter, EnumVariantNames};
1316

1417
use crate::{
15-
board::{BoardPrefab, BoardState, Element, Match},
18+
board::{
19+
BoardPrefab, BoardState, Element, Match, Tile, BETWEEN_MATCH_DELAY, MATCH_START_DELAY,
20+
},
1621
cards::{CardsPrefab, CardsState},
22+
particles::ParticleEmitter,
1723
player::{Player, Spell},
1824
prefab::{spawn, Prefab},
1925
transitions::{FadeScreenPrefab, TransitionDirection, TransitionEnd},
@@ -50,14 +56,17 @@ impl Plugin for BattlePlugin {
5056
ConditionSet::new()
5157
.run_in_state(BattleState::PlayerTurn)
5258
.with_system(track_matches)
59+
.with_system(animate_matches)
5360
.with_system(start_outtro)
5461
.with_system(kill_enemies)
5562
.with_system(end_player_turn.run_in_state(BoardState::End))
5663
.into(),
5764
)
5865
.add_enter_system(
5966
BoardState::End,
60-
player_attack.run_in_state(BattleState::PlayerTurn),
67+
player_attack
68+
.chain(animate_attack)
69+
.run_in_state(BattleState::PlayerTurn),
6170
)
6271
.add_enter_system(BattleState::EnemyTurn, enemies_attack)
6372
.add_system_set(
@@ -301,6 +310,146 @@ fn track_matches(mut events: EventReader<Match>, mut matches: ResMut<Matches>) {
301310
matches.0.extend(events.iter().cloned());
302311
}
303312

313+
fn animate_matches(
314+
mut events: EventReader<Match>,
315+
mut commands: Commands,
316+
tiles: Query<&GlobalTransform, With<Tile>>,
317+
mut materials: ResMut<Assets<StandardMaterial>>,
318+
asset_server: Res<AssetServer>,
319+
player: Res<Player>,
320+
) {
321+
if let Some(spell) = player.active_spell.as_ref() {
322+
let start_delay = Duration::from_secs_f32(MATCH_START_DELAY);
323+
let delay_between_matches = Duration::from_secs_f32(BETWEEN_MATCH_DELAY);
324+
325+
let mut delay = start_delay;
326+
for event in events
327+
.iter()
328+
.filter(|x| spell.elements.contains(&x.element))
329+
{
330+
let material = materials.add(StandardMaterial {
331+
base_color: event.element.color(),
332+
base_color_texture: Some(asset_server.load("particles/star_06.png")),
333+
double_sided: true,
334+
unlit: true,
335+
alpha_mode: AlphaMode::Blend,
336+
..default()
337+
});
338+
339+
for tile in &event.tiles {
340+
let transform = tiles.get(*tile).unwrap();
341+
342+
let transform = transform.compute_transform() * Transform::from_xyz(0.0, 0.0, 1.0);
343+
commands
344+
.spawn_bundle(SpatialBundle {
345+
transform,
346+
..default()
347+
})
348+
.insert(BOARD_LAYER)
349+
.insert(DelayedDespawn::from_seconds(0.7))
350+
.insert(Animator::new(Delay::new(delay).then(Tween::new(
351+
EaseFunction::QuadraticInOut,
352+
TweeningType::Once,
353+
Duration::from_secs_f32(0.5),
354+
TransformPositionLens {
355+
start: transform.translation,
356+
end: Vec3::new(0.0, -2.5, 1.0),
357+
},
358+
))))
359+
.with_children(|c| {
360+
c.spawn_bundle(SpatialBundle::default())
361+
.insert(ParticleEmitter {
362+
material: material.clone(),
363+
timer: Timer::from_seconds(1.0 / 200.0, true),
364+
size_range: 0.2..0.3,
365+
velocity_range: -0.01..0.01,
366+
lifetime_range: 0.5..1.0,
367+
particles_track: true,
368+
});
369+
370+
c.spawn_bundle(SpatialBundle::default())
371+
.insert(ParticleEmitter {
372+
material: material.clone(),
373+
timer: Timer::from_seconds(1.0 / 100.0, true),
374+
size_range: 0.2..0.3,
375+
velocity_range: -0.01..0.01,
376+
lifetime_range: 0.2..0.5,
377+
particles_track: false,
378+
});
379+
});
380+
381+
delay += delay_between_matches;
382+
}
383+
}
384+
}
385+
}
386+
387+
fn animate_attack(
388+
matches: Res<Matches>,
389+
mut commands: Commands,
390+
mut materials: ResMut<Assets<StandardMaterial>>,
391+
asset_server: Res<AssetServer>,
392+
player: Res<Player>,
393+
) {
394+
if let Some(spell) = player.active_spell.as_ref() {
395+
for event in matches
396+
.0
397+
.iter()
398+
.filter(|x| spell.elements.contains(&x.element))
399+
{
400+
let material = materials.add(StandardMaterial {
401+
base_color: event.element.color(),
402+
base_color_texture: Some(asset_server.load("particles/star_06.png")),
403+
double_sided: true,
404+
unlit: true,
405+
alpha_mode: AlphaMode::Blend,
406+
..default()
407+
});
408+
409+
for _ in &event.tiles {
410+
let transform = Transform::from_xyz(0.0, -2.5, 1.0);
411+
commands
412+
.spawn_bundle(SpatialBundle {
413+
transform,
414+
..default()
415+
})
416+
.insert(BOARD_LAYER)
417+
.insert(DelayedDespawn::from_seconds(0.7))
418+
.insert(Animator::new(Tween::new(
419+
EaseFunction::QuadraticInOut,
420+
TweeningType::Once,
421+
Duration::from_secs_f32(0.5),
422+
TransformPositionLens {
423+
start: transform.translation,
424+
end: Vec3::new(0.0, 2.1, 1.0),
425+
},
426+
)))
427+
.with_children(|c| {
428+
c.spawn_bundle(SpatialBundle::default())
429+
.insert(ParticleEmitter {
430+
material: material.clone(),
431+
timer: Timer::from_seconds(1.0 / 200.0, true),
432+
size_range: 0.2..0.3,
433+
velocity_range: -0.01..0.01,
434+
lifetime_range: 0.5..1.0,
435+
particles_track: true,
436+
});
437+
438+
c.spawn_bundle(SpatialBundle::default())
439+
.insert(ParticleEmitter {
440+
material: material.clone(),
441+
timer: Timer::from_seconds(1.0 / 100.0, true),
442+
size_range: 0.2..0.3,
443+
velocity_range: -0.01..0.01,
444+
lifetime_range: 0.2..0.5,
445+
particles_track: false,
446+
});
447+
});
448+
}
449+
}
450+
}
451+
}
452+
304453
fn player_attack(
305454
mut enemies: Query<(&mut Enemy, &mut EnemyAnimator, &EnemyAnimations)>,
306455
mut animation_players: Query<&mut AnimationPlayer>,

src/board.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,13 @@ fn match_gems(
378378
events.send_batch(matches.into_iter());
379379
}
380380

381+
pub const MATCH_START_DELAY: f32 = 0.1;
382+
pub const BETWEEN_MATCH_DELAY: f32 = 0.1;
383+
381384
fn destroy_matches(mut events: EventReader<Match>, tiles: Query<&Tile>, mut commands: Commands) {
382-
let start_delay = Duration::from_secs_f32(0.1);
385+
let start_delay = Duration::from_secs_f32(MATCH_START_DELAY);
383386
let delay_between_gems = Duration::from_secs_f32(0.0);
384-
let delay_between_matches = Duration::from_secs_f32(0.2);
387+
let delay_between_matches = Duration::from_secs_f32(BETWEEN_MATCH_DELAY);
385388
let animation_time = Duration::from_secs_f32(0.1);
386389

387390
let mut delay = start_delay;

src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@ use board::{BoardPlugin, BoardState};
55
use cards::{CardPlugin, CardsState};
66
use iyes_loopless::prelude::*;
77
use main_state::{MainState, MainStatePlugin};
8+
use particles::ParticlesPlugin;
89
use std::{fmt::Debug, hash::Hash};
910
use transitions::TransitionPlugin;
1011
use utils::UtilsPlugin;
1112

13+
#[cfg(target_arch = "wasm32")]
14+
mod wasm;
15+
1216
mod battle;
1317
mod board;
1418
mod cards;
1519
mod main_state;
20+
mod particles;
1621
mod player;
1722
mod prefab;
1823
mod transitions;
1924
mod tween_untils;
2025
mod ui;
21-
mod utils;
22-
23-
#[cfg(target_arch = "wasm32")]
24-
mod wasm;
26+
pub mod utils;
2527

2628
pub fn build_app() -> App {
2729
let mut app = App::new();
@@ -51,6 +53,7 @@ pub fn build_app() -> App {
5153
.add_plugin(BattlePlugin)
5254
.add_plugin(TransitionPlugin)
5355
.add_plugin(MainStatePlugin)
56+
.add_plugin(ParticlesPlugin)
5457
.add_system(log_states::<BoardState>)
5558
.add_system(log_states::<BattleState>)
5659
.add_system(log_states::<MainState>)

src/particles.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use std::ops::Range;
2+
3+
use bevy::{
4+
pbr::{NotShadowCaster, NotShadowReceiver},
5+
prelude::*,
6+
render::view::RenderLayers,
7+
};
8+
9+
use crate::utils::square_mesh;
10+
11+
pub struct ParticlesPlugin;
12+
13+
impl Plugin for ParticlesPlugin {
14+
fn build(&self, app: &mut App) {
15+
app.add_system(emit_particles).add_system(move_particles);
16+
}
17+
}
18+
19+
#[derive(Component)]
20+
pub struct Particle {
21+
pub lifetime: Timer,
22+
pub velocity: Vec3,
23+
}
24+
25+
#[derive(Component)]
26+
pub struct ParticleEmitter {
27+
pub material: Handle<StandardMaterial>,
28+
pub timer: Timer,
29+
pub size_range: Range<f32>,
30+
pub velocity_range: Range<f32>,
31+
// in seconds
32+
pub lifetime_range: Range<f32>,
33+
pub particles_track: bool,
34+
}
35+
36+
fn emit_particles(
37+
mut emitters: Query<(
38+
Entity,
39+
&mut ParticleEmitter,
40+
&GlobalTransform,
41+
Option<&RenderLayers>,
42+
)>,
43+
mut commands: Commands,
44+
time: Res<Time>,
45+
) {
46+
let mut rng = fastrand::Rng::new();
47+
for (entity, mut emitter, transform, render_layers) in &mut emitters {
48+
for _ in 0..(emitter.timer.tick(time.delta()).times_finished_this_tick()) {
49+
let lifetime = random_in_range(&emitter.lifetime_range, &mut rng);
50+
let size = random_in_range(&emitter.size_range, &mut rng);
51+
let velocity = Vec2::new(
52+
random_in_range(&emitter.velocity_range, &mut rng),
53+
random_in_range(&emitter.velocity_range, &mut rng),
54+
)
55+
.extend(0.0);
56+
57+
let particle = commands
58+
.spawn_bundle(PbrBundle {
59+
mesh: square_mesh(),
60+
material: emitter.material.clone(),
61+
transform: if emitter.particles_track {
62+
default()
63+
} else {
64+
transform.compute_transform()
65+
}
66+
.with_scale(Vec3::splat(size)),
67+
..default()
68+
})
69+
.insert(Particle {
70+
lifetime: Timer::from_seconds(lifetime, false),
71+
velocity,
72+
})
73+
.insert(NotShadowCaster)
74+
.insert(NotShadowReceiver)
75+
.id();
76+
77+
if emitter.particles_track {
78+
commands.entity(entity).add_child(particle);
79+
}
80+
81+
if let Some(render_layers) = render_layers {
82+
commands.entity(particle).insert(*render_layers);
83+
}
84+
}
85+
}
86+
}
87+
88+
fn random_in_range(range: &Range<f32>, rng: &mut fastrand::Rng) -> f32 {
89+
rng.f32() * (range.end - range.start) + range.start
90+
}
91+
92+
fn move_particles(
93+
mut particles: Query<(Entity, &mut Particle, &mut Transform)>,
94+
mut commands: Commands,
95+
time: Res<Time>,
96+
) {
97+
for (entity, mut particle, mut transform) in &mut particles {
98+
if particle.lifetime.tick(time.delta()).finished() {
99+
commands.entity(entity).despawn();
100+
} else {
101+
transform.translation += particle.velocity;
102+
}
103+
}
104+
}

src/utils.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{hash::Hash, time::Duration};
22

33
use bevy::{
44
asset::HandleId,
5-
ecs::system::AsSystemLabel,
5+
ecs::{query::QueryEntityError, system::AsSystemLabel},
66
pbr::{NotShadowCaster, NotShadowReceiver},
77
prelude::{shape::Quad, *},
88
reflect::TypeUuid,
@@ -24,12 +24,20 @@ impl Plugin for UtilsPlugin {
2424
.add_event::<WorldCursorEvent>()
2525
.add_startup_system(add_meshes)
2626
.add_startup_system(add_materials)
27-
.add_system(delayed_despawn)
27+
.add_stage_before(
28+
CoreStage::PostUpdate,
29+
"delayed_despawn",
30+
SystemStage::parallel(),
31+
)
32+
.add_system_to_stage("delayed_despawn", delayed_despawn)
2833
.add_system_to_stage(
2934
CoreStage::PostUpdate,
3035
update_progress.before(TransformSystem::TransformPropagate),
3136
)
32-
.add_system_to_stage(CoreStage::PostUpdate, propagate_render_layers)
37+
.add_system_to_stage(
38+
"delayed_despawn",
39+
propagate_render_layers.before(delayed_despawn),
40+
)
3341
.add_system_to_stage(CoreStage::PreUpdate, update_world_cursors)
3442
.add_system_to_stage(
3543
CoreStage::PreUpdate,
@@ -153,12 +161,16 @@ fn propagate_render_layers(
153161

154162
while !children.is_empty() {
155163
for child in std::mem::take(&mut children) {
156-
if let Ok(mut child_layer) = layers.get_mut(child) {
157-
if *child_layer != layer {
158-
*child_layer = layer;
164+
match layers.get_mut(child) {
165+
Ok(mut child_layer) => {
166+
if *child_layer != layer {
167+
*child_layer = layer;
168+
}
159169
}
160-
} else {
161-
commands.entity(child).insert(layer);
170+
Err(QueryEntityError::QueryDoesNotMatch(entity)) => {
171+
commands.entity(entity).insert(layer);
172+
}
173+
_ => {}
162174
}
163175

164176
children.extend(children_query.get(child).into_iter().flatten().cloned());

0 commit comments

Comments
 (0)