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, }, )); } _ => (), }); }