[solved] Custom Unity 3D rotational issues
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:
That's for pitching down, I do the same for all the other rotations too (code later)
Then dampen and clamp the shipAngularVelocity:
Then apply the resultant shipAngularVelocity with:
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:)
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'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
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.
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.