eased_motion/
eased_motion.rs1use std::f32::consts::FRAC_PI_2;
4
5use bevy::{
6 animation::{animated_field, AnimatedBy, 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
51 .entity(cube_entity)
52 .insert((animation_target_id, AnimatedBy(cube_entity)));
53
54 commands.spawn((
56 PointLight {
57 shadow_maps_enabled: true,
58 intensity: 10_000_000.,
59 range: 100.0,
60 ..default()
61 },
62 Transform::from_xyz(8., 16., 8.),
63 ));
64
65 commands.spawn((
67 Mesh3d(meshes.add(Plane3d::default().mesh().size(50., 50.))),
68 MeshMaterial3d(materials.add(Color::from(SILVER))),
69 ));
70
71 commands.spawn((
73 Camera3d::default(),
74 Transform::from_xyz(0., 6., 12.).looking_at(Vec3::new(0., 1.5, 0.), Vec3::Y),
75 ));
76}
77
78struct AnimationInfo {
80 target_name: Name,
82 target_id: AnimationTargetId,
84 graph: Handle<AnimationGraph>,
86 node_index: AnimationNodeIndex,
88}
89
90impl AnimationInfo {
91 fn create(
93 animation_graphs: &mut Assets<AnimationGraph>,
94 animation_clips: &mut Assets<AnimationClip>,
95 ) -> AnimationInfo {
96 let animation_target_name = Name::new("Cube");
98 let animation_target_id = AnimationTargetId::from_name(&animation_target_name);
99
100 let mut animation_clip = AnimationClip::default();
102
103 let animation_domain = interval(0.0, 3.0).unwrap();
105
106 let translation_curve = EasingCurve::new(
109 vec3(-6., 2., 0.),
110 vec3(6., 2., 0.),
111 EaseFunction::CubicInOut,
112 )
113 .reparametrize_linear(animation_domain)
114 .expect("this curve has bounded domain, so this should never fail")
115 .ping_pong()
116 .expect("this curve has bounded domain, so this should never fail");
117
118 let rotation_curve = EasingCurve::new(
122 Quat::IDENTITY,
123 Quat::from_rotation_y(FRAC_PI_2),
124 EaseFunction::ElasticInOut,
125 )
126 .reparametrize_linear(interval(0.0, 4.0).unwrap())
127 .expect("this curve has bounded domain, so this should never fail");
128
129 animation_clip.add_curve_to_target(
130 animation_target_id,
131 AnimatableCurve::new(animated_field!(Transform::translation), translation_curve),
132 );
133 animation_clip.add_curve_to_target(
134 animation_target_id,
135 AnimatableCurve::new(animated_field!(Transform::rotation), rotation_curve),
136 );
137
138 let animation_clip_handle = animation_clips.add(animation_clip);
140
141 let (animation_graph, animation_node_index) =
143 AnimationGraph::from_clip(animation_clip_handle);
144 let animation_graph_handle = animation_graphs.add(animation_graph);
145
146 AnimationInfo {
147 target_name: animation_target_name,
148 target_id: animation_target_id,
149 graph: animation_graph_handle,
150 node_index: animation_node_index,
151 }
152 }
153}