bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.29k stars 3.58k forks source link

2d rotations are cumbersome #14525

Open UkoeHB opened 3 months ago

UkoeHB commented 3 months ago

How can Bevy's documentation be improved?

The docs for Vec2, Dir2, Rot2, and Quat are poorly cross-linked/associated. This means it is quite a headache for new users of these APIs to figure out how to do conversions, and some of the conversions seem needlessly convoluted.

Here is the incantation to get Quat from Dir2 in 2d space:

let rotation = Quat::from_rotation_z(direction.rotation_from_x().as_radians());

Here is some code to place an entity at a random radial position around another entity:

let direction = rng.gen_range((0.)..TAU);
let mut point_transform = Transform::from_translation(Vec3::default().with_x(spawn_radius));
point_transform.translate_around(Vec3::default(), Quat::from_rotation_z(direction));

Here I'm getting the Aabb zone for doing 2d projectile intersection tests. Yes, Aabb2d panics randomly if you don't correct by PI - rotation...

let mut rotation = Quat::default().angle_between(zone_transform.rotation.normalize());
if rotation > PI / 2. {
    rotation = PI - rotation;
}
rotation = rotation.clamp(0., PI / 2.);
let entity_aabb = zone_aabb
    .get_2d_from_vec(Vec2::default())
    .transformed_by(zone_transform.translation.truncate(), rotation);
let entity_aabb = AabbCast2d::new(
    entity_aabb,
    Vec2::default(),
    Dir2::new(last_pos - zone_transform.translation.truncate())
        .unwrap_or(Dir2::new_unchecked(Vec2::default().with_x(1.))),
    (zone_transform.translation.truncate() - last_pos).length(),
);
benfrankel commented 3 months ago

I believe direction.rotation_from_x().as_radians() == direction.to_angle(), not in disagreement with the issue though. Dir2::to_quat may make this a little easier as well:

pub trait Dir2ExtToQuat {
    fn to_quat(self) -> Quat;
}

impl Dir2ExtToQuat for Dir2 {
    fn to_quat(self) -> Quat {
        Quat::from_rotation_z(self.to_angle())
    }
}
UkoeHB commented 3 months ago

I believe direction.rotation_from_x().as_radians() == direction.to_angle(), not in disagreement with the issue though.

There is no to_angle method on Dir2. It might be available via the Vec2 deref, but that doesn't show up in the docs for Dir2.

cactusdualcore commented 3 months ago

@benfrankel

Choosing the XY plane seems arbitrary. A more robust API probably shouldn't assume you're working in that plane. Although at least one other API (cursed be thee, Gizmos, silently converting 2D into the 3D XY-Plane) does this, I do find it awkward to not have four methods, one for each coordinate axis and one for any axes.

pub trait Dir2ExtToQuat {
  fn as_quat_around_x(self) -> Quat;

  fn as_quat_around_y(self) -> Quat;

  fn as_quat_around_z(self) -> Quat;

  fn as_quat_around(self, axis: Dir3) -> Quat;
}
benfrankel commented 3 months ago

For 3D games maybe that makes sense, but that doesn't help make things less cumbersome for 2D games where a rotation in the XZ plane is nonsense. We already have Quat::from_rotation_z(dir.to_angle()).

cactusdualcore commented 3 months ago

For 3D games maybe that makes sense, but that doesn't help make things less cumbersome for 2D games where a rotation in the XZ plane is nonsense. We already have `Quat::from_rotation_z(dir.to_angle()).

Rot2 is the rotation primitive in 2D. It can be directly multiplied with Vec2s for their rotated variants. Quat is the rotation primitive in 3D. It can't directly be applied to Vec2s and applying it to the Vec3s which include the Z-index will for most quaternions produce nonsensical results in a 2D plane.

I am convinced there are good use cases for converting a Rot2 to a Quat in 2D, but those people probably know what they're doing in the first place, because Quat isn't really intended for 2D.

benfrankel commented 3 months ago

I am convinced there are good use cases for converting a Rot2 to a Quat in 2D, but those people probably know what they're doing in the first place, because Quat isn't really intended for 2D.

Not really. You have to convert to Quat in 2D even if you don't know what you're doing, because it's what Transform uses.

cactusdualcore commented 3 months ago

I just checked the docs, you're right! I could have sworn there were from_*_axis_rotation constructors for Transform.