diff --git a/Makefile b/Makefile index d551ebd..da46f7d 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,10 @@ target/x86_64-unknown-linux-gnu/release/set: src/* target/x86_64-unknown-linux-gnu/quick/set: src/* cargo build --target x86_64-unknown-linux-gnu --features bevy/dynamic_linking --profile quick + +.PHONY: target/x86_64-unknown-linux-gnu/quick/examples/animation +target/x86_64-unknown-linux-gnu/quick/examples/animation: examples/animation.rs src/* + cargo run --example animation --target x86_64-unknown-linux-gnu --features bevy/dynamic_linking --profile quick ### ### @@ -46,6 +50,10 @@ native/debug/build: target/x86_64-unknown-linux-gnu/quick/set native/release/build: target/x86_64-unknown-linux-gnu/release/set ### +### +examples/animation: target/x86_64-unknown-linux-gnu/quick/examples/animation +### + ### native/debug/run: native/debug/build cargo run --target x86_64-unknown-linux-gnu --features bevy/dynamic_linking --profile quick diff --git a/assets/its-the-balatro-music.mp3 b/assets/its-the-balatro-music.mp3 new file mode 100644 index 0000000..1de7ef6 Binary files /dev/null and b/assets/its-the-balatro-music.mp3 differ diff --git a/assets/its-the-balatro-music.ogg b/assets/its-the-balatro-music.ogg new file mode 100644 index 0000000..60ee268 Binary files /dev/null and b/assets/its-the-balatro-music.ogg differ diff --git a/examples/animation.rs b/examples/animation.rs new file mode 100644 index 0000000..4c3866e --- /dev/null +++ b/examples/animation.rs @@ -0,0 +1,163 @@ +use bevy::{ + animation::{animated_field, AnimationTarget, AnimationTargetId}, + input::{keyboard::KeyboardInput, ButtonState}, + prelude::*, + utils::HashMap, +}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, init) + .add_systems(Update, switch) + .run(); +} + +#[derive(Resource)] +struct AnimationStore(HashMap); + +fn init( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + mut animations: ResMut>, + mut graphs: ResMut>, +) { + commands.spawn(Camera2d); + + commands.spawn(( + Text2d("Press 1/2/3 to trigger animation".into()), + Node { + top: Val::Px(-50.0), + height: Val::Percent(100.0), + ..default() + }, + )); + + let mut hash_map = HashMap::new(); + { + let mut a = AnimationClip::default(); + let animatable_curve = AnimatableCurve::new( + animated_field!(Transform::translation), + UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([ + Vec3::new(200.0, 200.0, 0.0), + Vec3::new(-200.0, 200.0, 0.0), + Vec3::new(-200.0, -200.0, 0.0), + Vec3::new(200.0, -200.0, 0.0), + // in case seamless looping is wanted, the last keyframe should + // be the same as the first one + Vec3::new(200.0, 200.0, 0.0), + ])) + .expect("should be able to build translation curve because we pass in valid samples"), + ); + a.add_curve_to_target( + AnimationTargetId::from_name(&Name::new("Circle")), + animatable_curve, + ); + let (graph, animation_index) = AnimationGraph::from_clip(animations.add(a)); + let graph_handle = AnimationGraphHandle(graphs.add(graph)); + hash_map.insert("Box".into(), (graph_handle, animation_index)); + }; + { + let mut a = AnimationClip::default(); + let animatable_curve = AnimatableCurve::new( + animated_field!(Transform::translation), + UnevenSampleAutoCurve::new([0.0, 1.0, 2.0].into_iter().zip([ + Vec3::new(200.0, 200.0, 0.0), + Vec3::new(-200.0, -200.0, 0.0), + Vec3::new(200.0, 200.0, 0.0), + ])) + .expect("should be able to build translation curve because we pass in valid samples"), + ); + a.add_curve_to_target( + AnimationTargetId::from_name(&Name::new("Circle")), + animatable_curve, + ); + let (graph, animation_index) = AnimationGraph::from_clip(animations.add(a)); + let graph_handle = AnimationGraphHandle(graphs.add(graph)); + hash_map.insert("Diag1".into(), (graph_handle, animation_index)); + }; + { + let mut a = AnimationClip::default(); + let animatable_curve = AnimatableCurve::new( + animated_field!(Transform::translation), + UnevenSampleAutoCurve::new([0.0, 1.0, 2.0].into_iter().zip([ + Vec3::new(-200.0, 200.0, 0.0), + Vec3::new(200.0, -200.0, 0.0), + // in case seamless looping is wanted, the last keyframe should + // be the same as the first one + Vec3::new(-200.0, 200.0, 0.0), + ])) + .expect("should be able to build translation curve because we pass in valid samples"), + ); + a.add_curve_to_target( + AnimationTargetId::from_name(&Name::new("Circle")), + animatable_curve, + ); + let (graph, animation_index) = AnimationGraph::from_clip(animations.add(a)); + let graph_handle = AnimationGraphHandle(graphs.add(graph)); + hash_map.insert("Diag2".into(), (graph_handle, animation_index)); + }; + + let store = AnimationStore(hash_map); + commands.insert_resource(store); + + let shape = meshes.add(Circle::new(50.0)); + let material = materials.add(Color::WHITE); + commands.spawn(( + Mesh2d(shape), + MeshMaterial2d(material), + Name::new("Circle"), + AnimationPlayer::default(), + )); +} + +fn switch( + mut commands: Commands, + mut evr_kbd: EventReader, + mut query: Query<(Entity, &Name, &mut AnimationPlayer)>, + store: Res, +) { + evr_kbd + .read() + .filter(|ev| ev.state == ButtonState::Pressed) + .for_each(|ev| match ev.key_code { + KeyCode::Digit1 => { + let (g, ai) = store.0.get("Box".into()).unwrap(); + let (e, n, mut ap) = query.single_mut(); + ap.stop_all().play(ai.clone()); + commands.entity(e).insert(( + g.clone(), + AnimationTarget { + id: AnimationTargetId::from_name(n), + player: e, + }, + )); + } + KeyCode::Digit2 => { + let (g, ai) = store.0.get("Diag1".into()).unwrap(); + let (e, n, mut ap) = query.single_mut(); + ap.stop_all().play(ai.clone()); + commands.entity(e).insert(( + g.clone(), + AnimationTarget { + id: AnimationTargetId::from_name(n), + player: e, + }, + )); + } + KeyCode::Digit3 => { + let (g, ai) = store.0.get("Diag2".into()).unwrap(); + let (e, n, mut ap) = query.single_mut(); + ap.stop_all().play(ai.clone()); + commands.entity(e).insert(( + g.clone(), + AnimationTarget { + id: AnimationTargetId::from_name(n), + player: e, + }, + )); + } + _ => (), + }); +} diff --git a/src/play.rs b/src/play.rs index 52d796f..2e2f2d3 100644 --- a/src/play.rs +++ b/src/play.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -use crate::{deck::Card, menu::UiMessage, GameState}; +use crate::{deck::Card, menu::UiMessage, setup::CardAnimations, GameState}; pub struct PlayPlugin; @@ -9,8 +9,7 @@ impl Plugin for PlayPlugin { app.add_event::() .add_observer(serve_cards) .add_systems(OnEnter(GameState::Main), deal_cards) - .add_systems(Update, deal_cards.run_if(set_added)) - .add_systems(Update, spin_cards); + .add_systems(Update, deal_cards.run_if(set_added)); } } @@ -33,26 +32,25 @@ pub(crate) struct PlayLocation { #[derive(Component)] pub(crate) struct SetNumber(pub u8); -/// Debug system rotating selected cards for visual flare -fn spin_cards(mut query: Query<&mut Transform, (With, With)>, time: Res