eased_motion/
eased_motion.rs1use std::f32::consts::FRAC_PI_2;
4
5use bevy::{
6 animation::{animated_field, AnimationTarget, AnimationTargetId},
7 color::palettes::css::{ORANGE, SILVER},
8 math::vec3,
9 prelude::*,
10};
11
12fn main() {
13 App::new()
14 .add_plugins(DefaultPlugins)
15 .add_systems(Startup, setup)
16 .run();
17}
18
19fn setup(
20 mut commands: Commands,
21 mut meshes: ResMut<Assets<Mesh>>,
22 mut materials: ResMut<Assets<StandardMaterial>>,
23 mut animation_graphs: ResMut<Assets<AnimationGraph>>,
24 mut animation_clips: ResMut<Assets<AnimationClip>>,
25) {
26 let AnimationInfo {
28 target_name: animation_target_name,
29 target_id: animation_target_id,
30 graph: animation_graph,
31 node_index: animation_node_index,
32 } = AnimationInfo::create(&mut animation_graphs, &mut animation_clips);
33
34 let mut animation_player = AnimationPlayer::default();
36 animation_player.play(animation_node_index).repeat();
37
38 let cube_entity = commands
40 .spawn((
41 Mesh3d(meshes.add(Cuboid::from_length(2.0))),
42 MeshMaterial3d(materials.add(Color::from(ORANGE))),
43 Transform::from_translation(vec3(-6., 2., 0.)),
44 animation_target_name,
45 animation_player,
46 AnimationGraphHandle(animation_graph),
47 ))
48 .id();
49
50 commands.entity(cube_entity).insert(AnimationTarget {
51 id: animation_target_id,
52 player: cube_entity,
53 });
54
55 commands.spawn((
57 PointLight {
58 shadows_enabled: true,
59 intensity: 10_000_000.,
60 range: 100.0,
61 ..default()
62 },
63 Transform::from_xyz(8., 16., 8.),
64 ));
65
66 commands.spawn((
68 Mesh3d(meshes.add(Plane3d::default().mesh().size(50., 50.))),
69 MeshMaterial3d(materials.add(Color::from(SILVER))),
70 ));
71
72 commands.spawn((
74 Camera3d::default(),
75 Transform::from_xyz(0., 6., 12.).looking_at(Vec3::new(0., 1.5, 0.), Vec3::Y),
76 ));
77}
78
79struct AnimationInfo {
81 target_name: Name,
83 target_id: AnimationTargetId,
85 graph: Handle<AnimationGraph>,
87 node_index: AnimationNodeIndex,
89}
90
91impl AnimationInfo {
92 fn create(
94 animation_graphs: &mut Assets<AnimationGraph>,
95 animation_clips: &mut Assets<AnimationClip>,
96 ) -> AnimationInfo {
97 let animation_target_name = Name::new("Cube");
99 let animation_target_id = AnimationTargetId::from_name(&animation_target_name);
100
101 let mut animation_clip = AnimationClip::default();
103
104 let animation_domain = interval(0.0, 3.0).unwrap();
106
107 let translation_curve = EasingCurve::new(
110 vec3(-6., 2., 0.),
111 vec3(6., 2., 0.),
112 EaseFunction::CubicInOut,
113 )
114 .reparametrize_linear(animation_domain)
115 .expect("this curve has bounded domain, so this should never fail")
116 .ping_pong()
117 .expect("this curve has bounded domain, so this should never fail");
118
119 let rotation_curve = EasingCurve::new(
123 Quat::IDENTITY,
124 Quat::from_rotation_y(FRAC_PI_2),
125 EaseFunction::ElasticInOut,
126 )
127 .reparametrize_linear(interval(0.0, 4.0).unwrap())
128 .expect("this curve has bounded domain, so this should never fail");
129
130 animation_clip.add_curve_to_target(
131 animation_target_id,
132 AnimatableCurve::new(animated_field!(Transform::translation), translation_curve),
133 );
134 animation_clip.add_curve_to_target(
135 animation_target_id,
136 AnimatableCurve::new(animated_field!(Transform::rotation), rotation_curve),
137 );
138
139 let animation_clip_handle = animation_clips.add(animation_clip);
141
142 let (animation_graph, animation_node_index) =
144 AnimationGraph::from_clip(animation_clip_handle);
145 let animation_graph_handle = animation_graphs.add(animation_graph);
146
147 AnimationInfo {
148 target_name: animation_target_name,
149 target_id: animation_target_id,
150 graph: animation_graph_handle,
151 node_index: animation_node_index,
152 }
153 }
154}