You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
6.9 KiB
Rust

use std::{f32::consts::PI, ops::RangeInclusive};
use bevy::{
animation::{animated_field, AnimationTarget, AnimationTargetId},
color::palettes::css::{BLACK, BLUE, GREEN, PINK, RED},
prelude::*,
utils::HashMap,
};
use crate::{
deck::Deck,
play::{self, Selected},
};
pub struct AnimationPlugin;
impl Plugin for AnimationPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, delayed_animation).add_systems(
Update,
animate_button.run_if(any_with_component::<AnimateButton>),
);
}
}
#[derive(Event, Clone)]
struct AnimationComplete;
#[derive(Resource, Default)]
pub(crate) struct AnimationStore {
pub store: HashMap<String, AnimationNodeIndex>,
pub card_graph: AnimationGraphHandle,
}
#[derive(Component)]
pub(crate) struct DelayedAnimation {
pub animation_index: AnimationNodeIndex,
pub delay: Timer,
}
pub(crate) fn setup_animations(
mut clips: ResMut<Assets<AnimationClip>>,
mut graphs: ResMut<Assets<AnimationGraph>>,
mut commands: Commands,
) {
let mut animation_store = AnimationStore::default();
let mut animation_graph = AnimationGraph::new();
let targets: Vec<AnimationTargetId> = Deck::iter_cards()
.map(|c| Name::new(format!("{}", c)))
.map(|n| AnimationTargetId::from_name(&n))
.collect();
// Rotation Animation
{
let mut animation = AnimationClip::default();
targets.iter().for_each(|target| {
let curve = AnimatableCurve::new(
animated_field!(Transform::rotation),
AnimatableKeyframeCurve::new([0.0, 5.0, 10.0, 15.0, 20.0].into_iter().zip([
Quat::IDENTITY,
Quat::from_axis_angle(Vec3::Z, PI / 2.),
Quat::from_axis_angle(Vec3::Z, PI / 2. * 2.),
Quat::from_axis_angle(Vec3::Z, PI / 2. * 3.),
Quat::IDENTITY,
]))
.expect("Rotation animation"),
);
animation.add_curve_to_target(*target, curve);
});
let animation_handle = clips.add(animation);
let animation_index = animation_graph.add_clip(animation_handle, 1.0, animation_graph.root);
animation_store
.store
.insert("rotate".into(), animation_index);
}
{
// For each spot on board
RangeInclusive::<u8>::new(0, 3).for_each(|x| {
RangeInclusive::<u8>::new(0, 3).for_each(|y| {
let a = Vec3::new(-125.0, 0.0, 2.0);
let b = play::card_placement(&play::PlayLocation { x, y });
let c = Vec3::new(125.0, 0.0, 0.0);
// Serve Deck -> Spot Animation
{
let mut animation = AnimationClip::default();
targets.iter().for_each(|target| {
let curve = AnimatableCurve::new(
animated_field!(Transform::translation),
AnimatableKeyframeCurve::new([0.0, 1.0].into_iter().zip([a, b]))
.expect("Serve Card animation"),
);
animation.add_curve_to_target(*target, curve);
});
animation.set_duration(1.0);
animation.add_event(1.0, AnimationComplete);
let animation_handle = clips.add(animation);
let animation_index =
animation_graph.add_clip(animation_handle, 1.0, animation_graph.root);
animation_store
.store
.insert(format!("deck->{:?}", (x, y)), animation_index);
}
// Spot -> Discard Animation
{
let mut animation = AnimationClip::default();
targets.iter().for_each(|target| {
let curve = AnimatableCurve::new(
animated_field!(Transform::translation),
AnimatableKeyframeCurve::new([0.0, 1.0].into_iter().zip([b, c]))
.expect("Serve Card animation"),
);
animation.add_curve_to_target(*target, curve);
});
animation.set_duration(1.0);
animation.add_event(1.0, AnimationComplete);
let animation_handle = clips.add(animation);
let animation_index =
animation_graph.add_clip(animation_handle, 1.0, animation_graph.root);
animation_store
.store
.insert(format!("{:?}->discard", (x, y)), animation_index);
}
});
});
}
animation_store.card_graph = AnimationGraphHandle(graphs.add(animation_graph));
commands.insert_resource(animation_store);
}
pub(crate) fn play_selected_animation(
trigger: Trigger<OnInsert, Selected>,
mut query: Query<&mut AnimationPlayer>,
store: Res<AnimationStore>,
) {
let ai = store.store.get("rotate".into()).unwrap();
query
.get_mut(trigger.entity())
.unwrap()
.start(ai.clone())
.repeat();
}
pub(crate) fn stop_selected_animation(
trigger: Trigger<OnRemove, Selected>,
mut query: Query<(&mut Transform, &mut AnimationPlayer)>,
) {
let (mut t, mut ap) = query.get_mut(trigger.entity()).unwrap();
ap.rewind_all().stop_all();
t.rotation = Quat::default();
}
fn delayed_animation(
mut query: Query<(Entity, &mut DelayedAnimation, &mut AnimationPlayer), With<AnimationTarget>>,
mut commands: Commands,
time: Res<Time>,
) {
query
.iter_mut()
.for_each(|(entity, mut delayed_animation, mut animation_player)| {
delayed_animation.delay.tick(time.delta());
if delayed_animation.delay.just_finished() {
animation_player.start(delayed_animation.animation_index);
commands.entity(entity).remove::<DelayedAnimation>();
}
});
}
#[derive(Component)]
pub(crate) struct AnimateButton;
pub(crate) fn animate_button(
query: Single<(&mut BackgroundColor, &mut Transform), With<AnimateButton>>,
time: Res<Time>,
) {
// t is a value in the range (0..=1)
let t = (f32::cos(time.elapsed_secs_wrapped()) + 1.0) / 2.0;
let (mut bg, mut tr) = query.into_inner();
// We want values from 0..3 for picking a color
bg.0 = match t * 3.0 {
0.0..=1.0 => Srgba::interpolate(&RED, &GREEN, t % 1.0).into(),
1.0..=2.0 => Srgba::interpolate(&GREEN, &BLUE, t % 1.0).into(),
2.0..=3.0 => Srgba::interpolate(&BLUE, &RED, t % 1.0).into(),
_ => todo!(),
};
// We want angles from -0.5..0.5 for angle
tr.rotation = Quat::from_axis_angle(Vec3::Z, (t / 2.0) - 0.25);
if t > 0.5 {
tr.scale += time.delta_secs() / 20.0;
} else {
tr.scale -= time.delta_secs() / 20.0;
}
}