[solved] Custom Unity 3D rotational issues

edited in Questions and Answers
Hi guys

So for GGJ I needed to build a custom physics type system independent of the rigidbodies physics system. Basically, I have a target transform, and I want to be able to apply a force to make it pitch, roll, yaw relative to its forward, as in like a spaceship.

I've gotten pretty far by this code:
if ((Input.GetKey(KeyCode.R)) || pitchDown)
        {
            Quaternion rotationChange = Quaternion.AngleAxis(turningSpeed, shipTransform.right);
            Vector3 eulerChange = rotationChange.eulerAngles;
            shipAngularVelocity += eulerChange;
        }

That's for pitching down, I do the same for all the other rotations too (code later)

Then dampen and clamp the shipAngularVelocity:
shipAngularVelocity = shipAngularVelocity * angularDrag;
        shipAngularVelocity = new Vector3(Mathf.Clamp(shipAngularVelocity.x, -topTurnSpeed, topTurnSpeed),
            Mathf.Clamp(shipAngularVelocity.y, -topTurnSpeed, topTurnSpeed),
            Mathf.Clamp(shipAngularVelocity.z, -topTurnSpeed, topTurnSpeed));


Then apply the resultant shipAngularVelocity with:
shipTransform.Rotate(shipAngularVelocity, Space.World);


The problem is weird - when I do any of the rotations in a single axis (so turn left and right, or just pitch up and down, or just roll left and right), it works just fine. But whenever any two axises are involved, the behaviour becomes unpredictable. It seems it's either
1. a world/local space issue, but I'm 100% certain the shipTransform transform is what it is, and the rotation is being applied correctly. Or
2. Gimble lock

Either way I've looked everywhere and I can't figure it out :( Anyone see anything about this code?

(Here are the rest of the rotational code:)

if ((Input.GetKey(KeyCode.R)) || pitchDown)
        {
            Quaternion rotationChange = Quaternion.AngleAxis(turningSpeed, shipTransform.right);
            Vector3 eulerChange = rotationChange.eulerAngles;
            shipAngularVelocity += eulerChange;
        }

        if ((Input.GetKey(KeyCode.F)) || pitchUp)
        {
            Quaternion rotationChange = Quaternion.AngleAxis(turningSpeed, shipTransform.right);
            Vector3 eulerChange = rotationChange.eulerAngles;
            shipAngularVelocity -= eulerChange;
        }

        if ((Input.GetKey(KeyCode.A)) || turnLeft)
        {
            Quaternion rotationChange = Quaternion.AngleAxis(turningSpeed, shipTransform.up);
            Vector3 eulerChange = rotationChange.eulerAngles;
            shipAngularVelocity -= eulerChange;
        }

        if ((Input.GetKey(KeyCode.D)) || turnRight)
        {
            Quaternion rotationChange = Quaternion.AngleAxis(turningSpeed, shipTransform.up);
            Vector3 eulerChange = rotationChange.eulerAngles;
            shipAngularVelocity += eulerChange;
        }

        if ((Input.GetKey(KeyCode.Q)) || rollLeft)
        {
            Quaternion rotationChange = Quaternion.AngleAxis(turningSpeed, shipTransform.forward);
            Vector3 eulerChange = rotationChange.eulerAngles;
            shipAngularVelocity -= eulerChange;
        }

        if ((Input.GetKey(KeyCode.E)) || rollRight)
        {
            Quaternion rotationChange = Quaternion.AngleAxis(turningSpeed, shipTransform.forward);
            Vector3 eulerChange = rotationChange.eulerAngles;
            shipAngularVelocity += eulerChange;

Comments

  • I haven't tried this, but my guess is that it's because even though the values you're feeding your angular velocity are relative to the ship's transform, the angular velocity itself isn't local.

    I'm not sure how you'd fix this without actually trying it out myself (because while a fix might technically be correct, it might feel wrong and cause other unexpected stuff), but my first guess would be to keep a local variable of the local angular velocity, apply my movement changes to that (your axes will then use Vector3.forward instead of shipTransform.forward, as it's local), and at the end of the calculations transform that into world angular velocity for the rigidbody. (It'd be something like transform.TransformDirection()).

    If you want the ship's existing angular velocity to bleed through (e.g. there's inertia or something), then I imagine you'd start the calculation off by converting your world angular velocity to local before applying your movement to it.

    Again, this is all guess work and I could be very wrong. :P
    Thanked by 1Tuism
  • Hmmm thanks for that, I'm not exactly sure what you mean, the angular velocity is definitely not localised - it's a general spin that's applied to the transform?

    My angular velocity variable above (shipAngularVelocity) is a variable, do you mean by local it should be on the ship transform? I don't see how that would make a difference, as it is just a vector3 variable.
  • edited
    But I've, for my own purposes, SOLVED THIS!

    It's very janky, but I've placed an invisible rigid body gameobject somewhere, made sure it's on a non-colliding layer, and applied forces to that, then simply forced the transform i need to control to have the same rotation as that dummy object.

    It's so hacky but it works so I guess I'll leave this in case it helps anyone.
    Thanked by 1Cyberwiz
Sign In or Register to comment.