Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/animation/animated_transform.rs
6592 views
1
//! Create and play an animation defined by code that operates on the [`Transform`] component.
2
3
use std::f32::consts::PI;
4
5
use bevy::{
6
animation::{animated_field, AnimationTarget, AnimationTargetId},
7
prelude::*,
8
};
9
10
fn main() {
11
App::new()
12
.add_plugins(DefaultPlugins)
13
.insert_resource(AmbientLight {
14
color: Color::WHITE,
15
brightness: 150.0,
16
..default()
17
})
18
.add_systems(Startup, setup)
19
.run();
20
}
21
22
fn setup(
23
mut commands: Commands,
24
mut meshes: ResMut<Assets<Mesh>>,
25
mut materials: ResMut<Assets<StandardMaterial>>,
26
mut animations: ResMut<Assets<AnimationClip>>,
27
mut graphs: ResMut<Assets<AnimationGraph>>,
28
) {
29
// Camera
30
commands.spawn((
31
Camera3d::default(),
32
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
33
));
34
35
// Light
36
commands.spawn((
37
PointLight {
38
intensity: 500_000.0,
39
..default()
40
},
41
Transform::from_xyz(0.0, 2.5, 0.0),
42
));
43
44
// Let's use the `Name` component to target entities. We can use anything we
45
// like, but names are convenient.
46
let planet = Name::new("planet");
47
let orbit_controller = Name::new("orbit_controller");
48
let satellite = Name::new("satellite");
49
50
// Creating the animation
51
let mut animation = AnimationClip::default();
52
// A curve can modify a single part of a transform: here, the translation.
53
let planet_animation_target_id = AnimationTargetId::from_name(&planet);
54
animation.add_curve_to_target(
55
planet_animation_target_id,
56
AnimatableCurve::new(
57
animated_field!(Transform::translation),
58
UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
59
Vec3::new(1.0, 0.0, 1.0),
60
Vec3::new(-1.0, 0.0, 1.0),
61
Vec3::new(-1.0, 0.0, -1.0),
62
Vec3::new(1.0, 0.0, -1.0),
63
// in case seamless looping is wanted, the last keyframe should
64
// be the same as the first one
65
Vec3::new(1.0, 0.0, 1.0),
66
]))
67
.expect("should be able to build translation curve because we pass in valid samples"),
68
),
69
);
70
// Or it can modify the rotation of the transform.
71
// To find the entity to modify, the hierarchy will be traversed looking for
72
// an entity with the right name at each level.
73
let orbit_controller_animation_target_id =
74
AnimationTargetId::from_names([planet.clone(), orbit_controller.clone()].iter());
75
animation.add_curve_to_target(
76
orbit_controller_animation_target_id,
77
AnimatableCurve::new(
78
animated_field!(Transform::rotation),
79
UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
80
Quat::IDENTITY,
81
Quat::from_axis_angle(Vec3::Y, PI / 2.),
82
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
83
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
84
Quat::IDENTITY,
85
]))
86
.expect("Failed to build rotation curve"),
87
),
88
);
89
// If a curve in an animation is shorter than the other, it will not repeat
90
// until all other curves are finished. In that case, another animation should
91
// be created for each part that would have a different duration / period.
92
let satellite_animation_target_id = AnimationTargetId::from_names(
93
[planet.clone(), orbit_controller.clone(), satellite.clone()].iter(),
94
);
95
animation.add_curve_to_target(
96
satellite_animation_target_id,
97
AnimatableCurve::new(
98
animated_field!(Transform::scale),
99
UnevenSampleAutoCurve::new(
100
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]
101
.into_iter()
102
.zip([
103
Vec3::splat(0.8),
104
Vec3::splat(1.2),
105
Vec3::splat(0.8),
106
Vec3::splat(1.2),
107
Vec3::splat(0.8),
108
Vec3::splat(1.2),
109
Vec3::splat(0.8),
110
Vec3::splat(1.2),
111
Vec3::splat(0.8),
112
]),
113
)
114
.expect("Failed to build scale curve"),
115
),
116
);
117
// There can be more than one curve targeting the same entity path.
118
animation.add_curve_to_target(
119
AnimationTargetId::from_names(
120
[planet.clone(), orbit_controller.clone(), satellite.clone()].iter(),
121
),
122
AnimatableCurve::new(
123
animated_field!(Transform::rotation),
124
UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
125
Quat::IDENTITY,
126
Quat::from_axis_angle(Vec3::Y, PI / 2.),
127
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
128
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
129
Quat::IDENTITY,
130
]))
131
.expect("should be able to build translation curve because we pass in valid samples"),
132
),
133
);
134
135
// Create the animation graph
136
let (graph, animation_index) = AnimationGraph::from_clip(animations.add(animation));
137
138
// Create the animation player, and set it to repeat
139
let mut player = AnimationPlayer::default();
140
player.play(animation_index).repeat();
141
142
// Create the scene that will be animated
143
// First entity is the planet
144
let planet_entity = commands
145
.spawn((
146
Mesh3d(meshes.add(Sphere::default())),
147
MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
148
// Add the animation graph and player
149
planet,
150
AnimationGraphHandle(graphs.add(graph)),
151
player,
152
))
153
.id();
154
commands.entity(planet_entity).insert((
155
AnimationTarget {
156
id: planet_animation_target_id,
157
player: planet_entity,
158
},
159
children![(
160
Transform::default(),
161
Visibility::default(),
162
orbit_controller,
163
AnimationTarget {
164
id: orbit_controller_animation_target_id,
165
player: planet_entity,
166
},
167
children![(
168
Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))),
169
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.9, 0.3))),
170
Transform::from_xyz(1.5, 0.0, 0.0),
171
AnimationTarget {
172
id: satellite_animation_target_id,
173
player: planet_entity,
174
},
175
satellite,
176
)],
177
)],
178
));
179
}
180
181