Skip to content

Commit 5dca00c

Browse files
tim-blackbirdinodentry
authored andcommitted
Add helper methods for rotating Transforms (bevyengine#5151)
# Objective Users often ask for help with rotations as they struggle with `Quat`s. `Quat` is rather complex and has a ton of verbose methods. ## Solution Add rotation helper methods to `Transform`. Co-authored-by: devil-ira <[email protected]>
1 parent 2c6c0eb commit 5dca00c

File tree

20 files changed

+111
-67
lines changed

20 files changed

+111
-67
lines changed

crates/bevy_animation/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ pub fn animation_player(
262262
rot_end = -rot_end;
263263
}
264264
// Rotations are using a spherical linear interpolation
265-
transform.rotation = Quat::from_array(rot_start.normalize().into())
266-
.slerp(Quat::from_array(rot_end.normalize().into()), lerp);
265+
transform.rotation =
266+
rot_start.normalize().slerp(rot_end.normalize(), lerp);
267267
}
268268
Keyframes::Translation(keyframes) => {
269269
let translation_start = keyframes[step_start];

crates/bevy_gltf/src/loader.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ async fn load_gltf<'a, 'b>(
368368
scale,
369369
} => Transform {
370370
translation: bevy_math::Vec3::from(translation),
371-
rotation: bevy_math::Quat::from_vec4(rotation.into()),
371+
rotation: bevy_math::Quat::from_array(rotation),
372372
scale: bevy_math::Vec3::from(scale),
373373
},
374374
},

crates/bevy_transform/src/components/transform.rs

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -195,20 +195,81 @@ impl Transform {
195195
self.local_z()
196196
}
197197

198-
/// Rotates the transform by the given rotation.
198+
/// Rotates this [`Transform`] by the given rotation.
199199
#[inline]
200200
pub fn rotate(&mut self, rotation: Quat) {
201201
self.rotation = rotation * self.rotation;
202202
}
203203

204-
/// Rotates this [`Transform`] around a point in space.
205-
/// If the point is a zero vector, this will rotate around the parent (if any) or the origin.
204+
/// Rotates this [`Transform`] around the given `axis` by `angle` (in radians).
205+
///
206+
/// If this [`Transform`] has a parent, the `axis` is relative to the rotation of the parent.
207+
#[inline]
208+
pub fn rotate_axis(&mut self, axis: Vec3, angle: f32) {
209+
self.rotate(Quat::from_axis_angle(axis, angle));
210+
}
211+
212+
/// Rotates this [`Transform`] around the X axis by `angle` (in radians).
213+
///
214+
/// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
215+
#[inline]
216+
pub fn rotate_x(&mut self, angle: f32) {
217+
self.rotate(Quat::from_rotation_x(angle));
218+
}
219+
220+
/// Rotates this [`Transform`] around the Y axis by `angle` (in radians).
221+
///
222+
/// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
223+
#[inline]
224+
pub fn rotate_y(&mut self, angle: f32) {
225+
self.rotate(Quat::from_rotation_y(angle));
226+
}
227+
228+
/// Rotates this [`Transform`] around the Z axis by `angle` (in radians).
229+
///
230+
/// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
231+
#[inline]
232+
pub fn rotate_z(&mut self, angle: f32) {
233+
self.rotate(Quat::from_rotation_z(angle));
234+
}
235+
236+
/// Rotates this [`Transform`] around its X axis by `angle` (in radians).
237+
#[inline]
238+
pub fn rotate_local_x(&mut self, angle: f32) {
239+
self.rotate_axis(self.local_x(), angle);
240+
}
241+
242+
/// Rotates this [`Transform`] around its Y axis by `angle` (in radians).
243+
#[inline]
244+
pub fn rotate_local_y(&mut self, angle: f32) {
245+
self.rotate_axis(self.local_y(), angle);
246+
}
247+
248+
/// Rotates this [`Transform`] around its Z axis by `angle` (in radians).
249+
#[inline]
250+
pub fn rotate_local_z(&mut self, angle: f32) {
251+
self.rotate_axis(self.local_z(), angle);
252+
}
253+
254+
/// Rotates this [`Transform`] around a `point` in space.
255+
///
256+
/// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent.
206257
#[inline]
207258
pub fn rotate_around(&mut self, point: Vec3, rotation: Quat) {
208259
self.translation = point + rotation * (self.translation - point);
209260
self.rotation *= rotation;
210261
}
211262

263+
/// Rotates this [`Transform`] so that its local negative z direction is toward
264+
/// `target` and its local y direction is toward `up`.
265+
#[inline]
266+
pub fn look_at(&mut self, target: Vec3, up: Vec3) {
267+
let forward = Vec3::normalize(self.translation - target);
268+
let right = up.cross(forward).normalize();
269+
let up = forward.cross(right);
270+
self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, forward));
271+
}
272+
212273
/// Multiplies `self` with `transform` component by component, returning the
213274
/// resulting [`Transform`]
214275
#[inline]
@@ -239,16 +300,6 @@ impl Transform {
239300
pub fn apply_non_uniform_scale(&mut self, scale_factor: Vec3) {
240301
self.scale *= scale_factor;
241302
}
242-
243-
/// Rotates this [`Transform`] so that its local z direction is toward
244-
/// `target` and its local y direction is toward `up`.
245-
#[inline]
246-
pub fn look_at(&mut self, target: Vec3, up: Vec3) {
247-
let forward = Vec3::normalize(self.translation - target);
248-
let right = up.cross(forward).normalize();
249-
let up = forward.cross(right);
250-
self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, forward));
251-
}
252303
}
253304

254305
impl Default for Transform {

examples/2d/rotation.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,8 @@ fn player_movement_system(
134134
movement_factor += 1.0;
135135
}
136136

137-
// create the change in rotation around the Z axis (perpendicular to the 2D plane of the screen)
138-
let rotation_delta = Quat::from_rotation_z(rotation_factor * ship.rotation_speed * TIME_STEP);
139-
// update the ship rotation with our rotation delta
140-
transform.rotation *= rotation_delta;
137+
// update the ship rotation around the Z axis (perpendicular to the 2D plane of the screen)
138+
transform.rotate_z(rotation_factor * ship.rotation_speed * TIME_STEP);
141139

142140
// get the ship's forward vector by applying the current rotation to the ships initial facing vector
143141
let movement_direction = transform.rotation * Vec3::Y;
@@ -168,7 +166,7 @@ fn snap_to_player_system(
168166

169167
// get the quaternion to rotate from the initial enemy facing direction to the direction
170168
// facing the player
171-
let rotate_to_player = Quat::from_rotation_arc(Vec3::Y, Vec3::from((to_player, 0.0)));
169+
let rotate_to_player = Quat::from_rotation_arc(Vec3::Y, to_player.extend(0.));
172170

173171
// rotate the enemy to face the player
174172
enemy_transform.rotation = rotate_to_player;
@@ -243,11 +241,7 @@ fn rotate_to_player_system(
243241
// calculate angle of rotation with limit
244242
let rotation_angle = rotation_sign * (config.rotation_speed * TIME_STEP).min(max_angle);
245243

246-
// get the quaternion to rotate from the current enemy facing direction towards the
247-
// direction facing the player
248-
let rotation_delta = Quat::from_rotation_z(rotation_angle);
249-
250244
// rotate the enemy to face the player
251-
enemy_transform.rotation *= rotation_delta;
245+
enemy_transform.rotate_z(rotation_angle);
252246
}
253247
}

examples/3d/lighting.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fn setup(
3434

3535
// left wall
3636
let mut transform = Transform::from_xyz(2.5, 2.5, 0.0);
37-
transform.rotate(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2));
37+
transform.rotate_z(std::f32::consts::FRAC_PI_2);
3838
commands.spawn_bundle(PbrBundle {
3939
mesh: meshes.add(Mesh::from(shape::Box::new(5.0, 0.15, 5.0))),
4040
transform,
@@ -47,7 +47,7 @@ fn setup(
4747
});
4848
// back (right) wall
4949
let mut transform = Transform::from_xyz(0.0, 2.5, -2.5);
50-
transform.rotate(Quat::from_rotation_x(std::f32::consts::FRAC_PI_2));
50+
transform.rotate_x(std::f32::consts::FRAC_PI_2);
5151
commands.spawn_bundle(PbrBundle {
5252
mesh: meshes.add(Mesh::from(shape::Box::new(5.0, 0.15, 5.0))),
5353
transform,
@@ -214,7 +214,7 @@ fn animate_light_direction(
214214
mut query: Query<&mut Transform, With<DirectionalLight>>,
215215
) {
216216
for mut transform in query.iter_mut() {
217-
transform.rotate(Quat::from_rotation_y(time.delta_seconds() * 0.5));
217+
transform.rotate_y(time.delta_seconds() * 0.5);
218218
}
219219
}
220220

examples/3d/parenting.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct Rotator;
1818
/// rotates the parent, which will result in the child also rotating
1919
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotator>>) {
2020
for mut transform in query.iter_mut() {
21-
transform.rotation *= Quat::from_rotation_x(3.0 * time.delta_seconds());
21+
transform.rotate_x(3.0 * time.delta_seconds());
2222
}
2323
}
2424

examples/3d/render_to_texture.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,15 @@ fn setup(
145145
/// Rotates the inner cube (first pass)
146146
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<FirstPassCube>>) {
147147
for mut transform in query.iter_mut() {
148-
transform.rotation *= Quat::from_rotation_x(1.5 * time.delta_seconds());
149-
transform.rotation *= Quat::from_rotation_z(1.3 * time.delta_seconds());
148+
transform.rotate_x(1.5 * time.delta_seconds());
149+
transform.rotate_z(1.3 * time.delta_seconds());
150150
}
151151
}
152152

153153
/// Rotates the outer cube (main pass)
154154
fn cube_rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<MainPassCube>>) {
155155
for mut transform in query.iter_mut() {
156-
transform.rotation *= Quat::from_rotation_x(1.0 * time.delta_seconds());
157-
transform.rotation *= Quat::from_rotation_y(0.7 * time.delta_seconds());
156+
transform.rotate_x(1.0 * time.delta_seconds());
157+
transform.rotate_y(0.7 * time.delta_seconds());
158158
}
159159
}

examples/3d/shapes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ fn setup(
5353
2.0,
5454
0.0,
5555
),
56+
rotation: Quat::from_rotation_x(-std::f32::consts::PI / 4.),
5657
..default()
5758
},
5859
..default()
@@ -86,8 +87,7 @@ fn setup(
8687

8788
fn rotate(mut query: Query<&mut Transform, With<Shape>>, time: Res<Time>) {
8889
for mut transform in query.iter_mut() {
89-
transform.rotation = Quat::from_rotation_y(time.seconds_since_startup() as f32 / 2.)
90-
* Quat::from_rotation_x(-std::f32::consts::PI / 4.);
90+
transform.rotate_y(time.delta_seconds() / 2.);
9191
}
9292
}
9393

examples/ecs/hierarchy.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,14 @@ fn rotate(
9494
let angle = std::f32::consts::PI / 2.0;
9595
for (parent, children) in parents_query.iter_mut() {
9696
if let Ok(mut transform) = transform_query.get_mut(parent) {
97-
transform.rotate(Quat::from_rotation_z(-angle * time.delta_seconds()));
97+
transform.rotate_z(-angle * time.delta_seconds());
9898
}
9999

100100
// To iterate through the entities children, just treat the Children component as a Vec
101101
// Alternatively, you could query entities that have a Parent component
102102
for child in children.iter() {
103103
if let Ok(mut transform) = transform_query.get_mut(*child) {
104-
transform.rotate(Quat::from_rotation_z(angle * 2.0 * time.delta_seconds()));
104+
transform.rotate_z(angle * 2.0 * time.delta_seconds());
105105
}
106106
}
107107

examples/games/alien_cake_addict.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ fn spawn_bonus(
348348
fn rotate_bonus(game: Res<Game>, time: Res<Time>, mut transforms: Query<&mut Transform>) {
349349
if let Some(entity) = game.bonus.entity {
350350
if let Ok(mut cake_transform) = transforms.get_mut(entity) {
351-
cake_transform.rotate(Quat::from_rotation_y(time.delta_seconds()));
351+
cake_transform.rotate_y(time.delta_seconds());
352352
cake_transform.scale = Vec3::splat(
353353
1.0 + (game.score as f32 / 10.0 * time.seconds_since_startup().sin() as f32).abs(),
354354
);

0 commit comments

Comments
 (0)