transform/transform.rs
1//! Shows multiple transformations of objects.
2
3use std::f32::consts::PI;
4
5use bevy::{color::palettes::basic::YELLOW, prelude::*};
6
7// A struct for additional data of for a moving cube.
8#[derive(Component)]
9struct CubeState {
10 start_pos: Vec3,
11 move_speed: f32,
12 turn_speed: f32,
13}
14
15// A struct adding information to a scalable entity,
16// that will be stationary at the center of the scene.
17#[derive(Component)]
18struct Center {
19 max_size: f32,
20 min_size: f32,
21 scale_factor: f32,
22}
23
24fn main() {
25 App::new()
26 .add_plugins(DefaultPlugins)
27 .add_systems(Startup, setup)
28 .add_systems(
29 Update,
30 (
31 move_cube,
32 rotate_cube,
33 scale_down_sphere_proportional_to_cube_travel_distance,
34 )
35 .chain(),
36 )
37 .run();
38}
39
40// Startup system to setup the scene and spawn all relevant entities.
41fn setup(
42 mut commands: Commands,
43 mut meshes: ResMut<Assets<Mesh>>,
44 mut materials: ResMut<Assets<StandardMaterial>>,
45) {
46 // Add an object (sphere) for visualizing scaling.
47 commands.spawn((
48 Mesh3d(meshes.add(Sphere::new(3.0).mesh().ico(32).unwrap())),
49 MeshMaterial3d(materials.add(Color::from(YELLOW))),
50 Transform::from_translation(Vec3::ZERO),
51 Center {
52 max_size: 1.0,
53 min_size: 0.1,
54 scale_factor: 0.05,
55 },
56 ));
57
58 // Add the cube to visualize rotation and translation.
59 // This cube will circle around the center_sphere
60 // by changing its rotation each frame and moving forward.
61 // Define a start transform for an orbiting cube, that's away from our central object (sphere)
62 // and rotate it so it will be able to move around the sphere and not towards it.
63 let cube_spawn =
64 Transform::from_translation(Vec3::Z * -10.0).with_rotation(Quat::from_rotation_y(PI / 2.));
65 commands.spawn((
66 Mesh3d(meshes.add(Cuboid::default())),
67 MeshMaterial3d(materials.add(Color::WHITE)),
68 cube_spawn,
69 CubeState {
70 start_pos: cube_spawn.translation,
71 move_speed: 2.0,
72 turn_speed: 0.2,
73 },
74 ));
75
76 // Spawn a camera looking at the entities to show what's happening in this example.
77 commands.spawn((
78 Camera3d::default(),
79 Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
80 ));
81
82 // Add a light source for better 3d visibility.
83 commands.spawn((
84 DirectionalLight::default(),
85 Transform::from_xyz(3.0, 3.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
86 ));
87}
88
89// This system will move the cube forward.
90fn move_cube(mut cubes: Query<(&mut Transform, &mut CubeState)>, timer: Res<Time>) {
91 for (mut transform, cube) in &mut cubes {
92 // Move the cube forward smoothly at a given move_speed.
93 let forward = transform.forward();
94 transform.translation += forward * cube.move_speed * timer.delta_secs();
95 }
96}
97
98// This system will rotate the cube slightly towards the center_sphere.
99// Due to the forward movement the resulting movement
100// will be a circular motion around the center_sphere.
101fn rotate_cube(
102 mut cubes: Query<(&mut Transform, &mut CubeState), Without<Center>>,
103 center_spheres: Query<&Transform, With<Center>>,
104 timer: Res<Time>,
105) {
106 // Calculate the point to circle around. (The position of the center_sphere)
107 let mut center: Vec3 = Vec3::ZERO;
108 for sphere in ¢er_spheres {
109 center += sphere.translation;
110 }
111 // Update the rotation of the cube(s).
112 for (mut transform, cube) in &mut cubes {
113 // Calculate the rotation of the cube if it would be looking at the sphere in the center.
114 let look_at_sphere = transform.looking_at(center, *transform.local_y());
115 // Interpolate between the current rotation and the fully turned rotation
116 // when looking a the sphere, with a given turn speed to get a smooth motion.
117 // With higher speed the curvature of the orbit would be smaller.
118 let incremental_turn_weight = cube.turn_speed * timer.delta_secs();
119 let old_rotation = transform.rotation;
120 transform.rotation = old_rotation.lerp(look_at_sphere.rotation, incremental_turn_weight);
121 }
122}
123
124// This system will scale down the sphere in the center of the scene
125// according to the traveling distance of the orbiting cube(s) from their start position(s).
126fn scale_down_sphere_proportional_to_cube_travel_distance(
127 cubes: Query<(&Transform, &CubeState), Without<Center>>,
128 mut centers: Query<(&mut Transform, &Center)>,
129) {
130 // First we need to calculate the length of between
131 // the current position of the orbiting cube and the spawn position.
132 let mut distances = 0.0;
133 for (cube_transform, cube_state) in &cubes {
134 distances += (cube_state.start_pos - cube_transform.translation).length();
135 }
136 // Now we use the calculated value to scale the sphere in the center accordingly.
137 for (mut transform, center) in &mut centers {
138 // Calculate the new size from the calculated distances and the centers scale_factor.
139 // Since we want to have the sphere at its max_size at the cubes spawn location we start by
140 // using the max_size as start value and subtract the distances scaled by a scaling factor.
141 let mut new_size: f32 = center.max_size - center.scale_factor * distances;
142
143 // The new size should also not be smaller than the centers min_size.
144 // Therefore the max value out of (new_size, center.min_size) is used.
145 new_size = new_size.max(center.min_size);
146
147 // Now scale the sphere uniformly in all directions using new_size.
148 // Here Vec3:splat is used to create a vector with new_size in x, y and z direction.
149 transform.scale = Vec3::splat(new_size);
150 }
151}