Animation example
parent
28890a870b
commit
be7627e9c0
Binary file not shown.
Binary file not shown.
@ -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<String, (AnimationGraphHandle, AnimationNodeIndex)>);
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
|
mut animations: ResMut<Assets<AnimationClip>>,
|
||||||
|
mut graphs: ResMut<Assets<AnimationGraph>>,
|
||||||
|
) {
|
||||||
|
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<KeyboardInput>,
|
||||||
|
mut query: Query<(Entity, &Name, &mut AnimationPlayer)>,
|
||||||
|
store: Res<AnimationStore>,
|
||||||
|
) {
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,10 +1,11 @@
|
|||||||
TODO:
|
TODO:
|
||||||
* "New Game" button
|
|
||||||
* Print "no set"/"too many cards" alert
|
|
||||||
* Guarentee there is always a set
|
|
||||||
* Better shuffling
|
* Better shuffling
|
||||||
|
* Make "set" button visually interesting when 3 cards selected
|
||||||
|
* Use Animation for rotating cards
|
||||||
|
* Animate cards deck -> board
|
||||||
|
* Animate cards deck -> set-pile
|
||||||
|
|
||||||
Later:
|
Later:
|
||||||
* View all cards with some indication of in-set
|
* View all cards with some indication of in-set
|
||||||
* Music!
|
* Music! (w/ sam)
|
||||||
* Make button(s) look pretty
|
* Make button(s) look pretty
|
||||||
|
|||||||
Loading…
Reference in New Issue