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 &center_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}