Hi all, I’ve spent the last few days mulling over this. I’m trying to achieve the same aircraft movement as in the mobile game Metalstorm, if you’ve played it before. For reference, I’m using Unity 6.0 LTS and coding in C#.
I’ve set up a simple prefab of an empty gameobject, with a 3D cube childed to it that has a rigidbody (Main Fuselage). Also childed to the parent empty gameobject, I have three empty gameobjects, which I call the transforms:
My player controller script is simple. It just adds a virtual joystick (on-screen for mobile). This sends the x and y player joystick input to a ship controller script, as a Vector2.
The ship controller script is as follows:
using UnityEngine;
using UnityEngine.EventSystems;
public class ShipMovement : MonoBehaviour
{
// Ship's Rigidbody component variable and pitch, yaw and roll variables
public Rigidbody shipRigidbody;
public Transform transformPitch;
public Transform transformYaw;
public Transform transformRoll;
// Ship's pitch, yaw and roll variables
private Quaternion shipPitchRotation;
private Quaternion shipYawRotation;
private Quaternion shipRollRotation;
private float shipRollAngle;
private float shipPitchSpeed = 15.0f; // TEMPORARY VALUE
private float shipYawSpeed = 15.0f; // TEMPORARY VALUE
private float shipRollSpeed = 0.5f; // TEMPORARY VALUE
private void FixedUpdate()
{
// Call ship movement methods
MoveShip();
}
public void PlayerShipMoveControl(Vector2 moveDirection)
{
// If there is a yaw input
if (moveDirection.x != 0.0f)
{
// Rotate the yaw transform in yaw
shipYawRotation = Quaternion.AngleAxis((moveDirection.x * shipYawSpeed * Time.fixedDeltaTime), Vector3.up);
transformYaw.localRotation *= shipYawRotation;
}
// If there is a pitch input
if (moveDirection.y != 0.0f)
{
// Rotate the pitch transform in pitch
shipPitchRotation = Quaternion.AngleAxis((-moveDirection.y * shipPitchSpeed * Time.fixedDeltaTime), Vector3.right);
transformPitch.localRotation *= shipPitchRotation;
}
// Calculate the ship roll angle based on the pitch and yaw inputs and only subtract 90° if there is input (to correct orientation)
if (moveDirection.y != 0.0f || moveDirection.x != 0.0f)
{
shipRollAngle = (Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg) - 90.0f;
}
else
{
shipRollAngle = 0.0f;
}
// If there is no input and the pitch transform is pointing downwards (the ship is up-side-down when the player lets go of the input)
if (shipRollAngle == 0.0f && (Vector3.Dot(transformPitch.up, Vector3.up) < 0))
{
// THE FOLLOWING CODE WORKS EXCEPT THE SHIP JUST FLIPS INSTANTLY:
shipPitchRotation = Quaternion.AngleAxis(180.0f, Vector3.forward);
transformPitch.localRotation *= shipPitchRotation;
}
// Rotate the roll transform with the calculated roll angle using a smooth linear interpolation
shipRollRotation = Quaternion.AngleAxis(shipRollAngle, Vector3.forward);
transformRoll.localRotation = Quaternion.Slerp(transformRoll.localRotation, shipRollRotation, shipRollSpeed * Time.fixedDeltaTime);
}
private void MoveShip()
{
// Rotate the ship's rigidbody to match the yaw, pitch and roll transforms (in this order, as required by Unity when working with Quaternion algebra)
shipRigidbody.MoveRotation(transformYaw.localRotation * transformPitch.localRotation * transformRoll.localRotation);
// Position the pitch, yaw and roll transforms on the ship's position
transformPitch.position = shipRigidbody.position;
transformYaw.position = shipRigidbody.position;
transformRoll.position = shipRigidbody.position;
}
}
The above works wonderfully well in terms of keeping the aircraft movement independent in yaw, pitch and roll. As the player moves the joystick, the aircraft rolls to the same angle and it yaws and pitches independently as well in the direction of the joystick. There is also no gimbal locking.
All seems to be solved until I get to the fact that when the aircraft is up-side-down (see lines 55 to 61 above), it doesn’t re-orient to go up-right. I want the game to automatically roll the ship 180° so that the pitch transform faces upwards as soon as the player releases the joystick. The above code appears to work but simply doesn’t as the ship immediately rotates 180° to face upwards. Literally from one frame to the next, it’s oriented upwards (it doesn’t smoothly roll to that position).
All attempts to Quaternion.Slerp it to roll into said position have failed as the movement goes haywire and the aircraft ends up pointing a random direction. If have even tried to use a single empty gameobject transform in my prefab to handle everything but then yaw, pitch and roll stop being independent of each other, which I don’t want.
Any pointers would be greatly appreciated. This is driving me mad. Thank you very much!