1use std::f32::consts::PI;
4
5use bevy::{
6 animation::{animated_field, AnimationTarget, AnimationTargetId},
7 prelude::*,
8};
9
10fn main() {
11 App::new()
12 .add_plugins(DefaultPlugins)
13 .insert_resource(AmbientLight {
14 color: Color::WHITE,
15 brightness: 150.0,
16 })
17 .add_systems(Startup, setup)
18 .run();
19}
20
21fn setup(
22 mut commands: Commands,
23 mut meshes: ResMut<Assets<Mesh>>,
24 mut materials: ResMut<Assets<StandardMaterial>>,
25 mut animations: ResMut<Assets<AnimationClip>>,
26 mut graphs: ResMut<Assets<AnimationGraph>>,
27) {
28 commands.spawn((
30 Camera3d::default(),
31 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
32 ));
33
34 commands.spawn((
36 PointLight {
37 intensity: 500_000.0,
38 ..default()
39 },
40 Transform::from_xyz(0.0, 2.5, 0.0),
41 ));
42
43 let planet = Name::new("planet");
46 let orbit_controller = Name::new("orbit_controller");
47 let satellite = Name::new("satellite");
48
49 let mut animation = AnimationClip::default();
51 let planet_animation_target_id = AnimationTargetId::from_name(&planet);
53 animation.add_curve_to_target(
54 planet_animation_target_id,
55 AnimatableCurve::new(
56 animated_field!(Transform::translation),
57 UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
58 Vec3::new(1.0, 0.0, 1.0),
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),
65 ]))
66 .expect("should be able to build translation curve because we pass in valid samples"),
67 ),
68 );
69 let orbit_controller_animation_target_id =
73 AnimationTargetId::from_names([planet.clone(), orbit_controller.clone()].iter());
74 animation.add_curve_to_target(
75 orbit_controller_animation_target_id,
76 AnimatableCurve::new(
77 animated_field!(Transform::rotation),
78 UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
79 Quat::IDENTITY,
80 Quat::from_axis_angle(Vec3::Y, PI / 2.),
81 Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
82 Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
83 Quat::IDENTITY,
84 ]))
85 .expect("Failed to build rotation curve"),
86 ),
87 );
88 let satellite_animation_target_id = AnimationTargetId::from_names(
92 [planet.clone(), orbit_controller.clone(), satellite.clone()].iter(),
93 );
94 animation.add_curve_to_target(
95 satellite_animation_target_id,
96 AnimatableCurve::new(
97 animated_field!(Transform::scale),
98 UnevenSampleAutoCurve::new(
99 [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]
100 .into_iter()
101 .zip([
102 Vec3::splat(0.8),
103 Vec3::splat(1.2),
104 Vec3::splat(0.8),
105 Vec3::splat(1.2),
106 Vec3::splat(0.8),
107 Vec3::splat(1.2),
108 Vec3::splat(0.8),
109 Vec3::splat(1.2),
110 Vec3::splat(0.8),
111 ]),
112 )
113 .expect("Failed to build scale curve"),
114 ),
115 );
116 animation.add_curve_to_target(
118 AnimationTargetId::from_names(
119 [planet.clone(), orbit_controller.clone(), satellite.clone()].iter(),
120 ),
121 AnimatableCurve::new(
122 animated_field!(Transform::rotation),
123 UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
124 Quat::IDENTITY,
125 Quat::from_axis_angle(Vec3::Y, PI / 2.),
126 Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
127 Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
128 Quat::IDENTITY,
129 ]))
130 .expect("should be able to build translation curve because we pass in valid samples"),
131 ),
132 );
133
134 let (graph, animation_index) = AnimationGraph::from_clip(animations.add(animation));
136
137 let mut player = AnimationPlayer::default();
139 player.play(animation_index).repeat();
140
141 let planet_entity = commands
144 .spawn((
145 Mesh3d(meshes.add(Sphere::default())),
146 MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
147 planet,
149 AnimationGraphHandle(graphs.add(graph)),
150 player,
151 ))
152 .id();
153 commands
154 .entity(planet_entity)
155 .insert(AnimationTarget {
156 id: planet_animation_target_id,
157 player: planet_entity,
158 })
159 .with_children(|p| {
160 p.spawn((
162 Transform::default(),
163 Visibility::default(),
164 orbit_controller,
165 AnimationTarget {
166 id: orbit_controller_animation_target_id,
167 player: planet_entity,
168 },
169 ))
170 .with_children(|p| {
171 p.spawn((
173 Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))),
174 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.9, 0.3))),
175 Transform::from_xyz(1.5, 0.0, 0.0),
176 AnimationTarget {
177 id: satellite_animation_target_id,
178 player: planet_entity,
179 },
180 satellite,
181 ));
182 });
183 });
184}