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 ..default()
17 })
18 .add_systems(Startup, setup)
19 .run();
20}
21
22fn 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 commands.spawn((
31 Camera3d::default(),
32 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
33 ));
34
35 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 planet = Name::new("planet");
47 let orbit_controller = Name::new("orbit_controller");
48 let satellite = Name::new("satellite");
49
50 let mut animation = AnimationClip::default();
52 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 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 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 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 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 let (graph, animation_index) = AnimationGraph::from_clip(animations.add(animation));
137
138 let mut player = AnimationPlayer::default();
140 player.play(animation_index).repeat();
141
142 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 planet,
150 AnimationGraphHandle(graphs.add(graph)),
151 player,
152 ))
153 .id();
154 commands
155 .entity(planet_entity)
156 .insert(AnimationTarget {
157 id: planet_animation_target_id,
158 player: planet_entity,
159 })
160 .with_children(|p| {
161 p.spawn((
163 Transform::default(),
164 Visibility::default(),
165 orbit_controller,
166 AnimationTarget {
167 id: orbit_controller_animation_target_id,
168 player: planet_entity,
169 },
170 ))
171 .with_children(|p| {
172 p.spawn((
174 Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))),
175 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.9, 0.3))),
176 Transform::from_xyz(1.5, 0.0, 0.0),
177 AnimationTarget {
178 id: satellite_animation_target_id,
179 player: planet_entity,
180 },
181 satellite,
182 ));
183 });
184 });
185}