Attitude Indicator for Flight Sim

I’m making a flight-sim-esque game, and I’ve created a HUD-style attitude indicator. So far I’ve implemented an Angle-of-Attack indicator and a bank indicator and I’m trying to add a pitch indicator now.

I’m calculating bank using a bit of a trick. I have a helper transform, and I set its forward vector equal to the vehicle forward. Exploiting the fact that the up vector always tries to orient to world up, I can calculate the vehicle’s bank by comparing the helper’s up vector to the vehicle’s up vector:

AttitudeHelper.forward = transform.forward;

if (Vector3.Dot(transform.up, AttitudeHelper.right) > 0)
	{myAttitudeIndicator.bank = -Vector3.Angle(AttitudeHelper.up, transform.up);}
else
	{myAttitudeIndicator.bank = Vector3.Angle(AttitudeHelper.up, transform.up);}

So far so good - the bank indicator is reliable, so I thought I’d try the same trick by setting the helper’s right vector to the vehicle’s right vector, then comparing their up vectors:

AttitudeHelper.right = transform.right;

if (Vector3.Dot(transform.up, AttitudeHelper.forward) > 0)
	{myAttitudeIndicator.pitch = -Vector3.Angle(AttitudeHelper.up, transform.up);}
else
	{myAttitudeIndicator.pitch = Vector3.Angle(AttitudeHelper.up, transform.up);}

This creates a pitch indicator that is only right when the bank is zero. If the wings are banked even a little, I get weird results.

In order to calculate the pitch, I need a vector with the same heading as the vehicle’s, but flat. I can’t think of a way to do this. I haven’t been through the maths, I’ve just exploited a fluke of the way Unity sets rotations to get a correct bank value, but it doesn’t work for pitch, so I have to do some real maths now. Any ideas?

EDIT: I was actually comparing the up vectors - I’ve changed the text to reflect this, although this code has exactly the same problems:

AttitudeHelper.forward = transform.forward;

if (Vector3.Dot(transform.up, AttitudeHelper.right) > 0)
	{myAttitudeIndicator.bank = -Vector3.Angle(AttitudeHelper.right, transform.right);}
else
	{myAttitudeIndicator.bank = Vector3.Angle(AttitudeHelper.right, transform.right);}

AttitudeHelper.right = transform.right;

if (Vector3.Dot(transform.up, AttitudeHelper.forward) > 0)
	{myAttitudeIndicator.pitch = -Vector3.Angle(AttitudeHelper.forward, transform.forward);}
else
	{myAttitudeIndicator.pitch = Vector3.Angle(AttitudeHelper.forward, transform.forward);}

Well, I've solved it on my own. Here's the complete code, to be attached to the vehicle's chassis:

var AOAIndicator : GUITexture;
var AxisIndicator : GUITexture;
var AOAForward : Texture2D;
var AOAReverse : Texture2D;
var myAttitudeIndicator : AttitudeIndicator;
var BankHelper : Transform;
var PitchHelper : Transform;

private var localvelocity : Vector3;
private var AOAPosition : Vector2;

function Start()
{
    AxisIndicator.transform.position = Vector3(0.5, 0.5, 1);
}

function Update ()
{
    if (rigidbody.velocity.sqrMagnitude < 0.1)
        {AOAIndicator.transform.position = Vector3(0.5,0.5,1);}
    else
    {
        AOAIndicator.transform.position = Camera.main.WorldToViewportPoint(Camera.main.transform.position + rigidbody.velocity);

        if (AOAIndicator.transform.position.z < 0.0)
            {AOAIndicator.guiTexture.texture = AOAReverse;}
        else
            {AOAIndicator.guiTexture.texture = AOAForward;}
    }

    BankHelper.forward = transform.forward;

    if (Vector3.Dot(BankHelper.right, transform.up) > 0)
        {myAttitudeIndicator.bank = -Vector3.Angle(BankHelper.right, transform.right);}
    else
        {myAttitudeIndicator.bank = Vector3.Angle(BankHelper.right, transform.right);}

    PitchHelper.forward = Vector3(transform.forward.x, 0, transform.forward.z);

    if (Vector3.Dot(PitchHelper.up, transform.forward) > 0)
        {myAttitudeIndicator.pitch = Vector3.Angle(PitchHelper.forward, transform.forward);}
    else
        {myAttitudeIndicator.pitch = -Vector3.Angle(PitchHelper.forward, transform.forward);}

}

As far as I can tell, the Angle-of-Attack indicator (AOAIndicator) only works if the camera is perfectly aligned to the vehicle in question, so floaty third-person cameras are out of the question if you want this code to work properly without modifications.

As for the attitude indicator class, it's a modification of Duck's RotatableGuiItem class, which you can find here: http://answers.unity3d.com/questions/11022/how-to-rotate-gui-textures.html

It's not finished yet - for one thing it doesn't move the pitch indicator the correct amount against the world view. Hopefully I'll remember to post it here when I'm finished, so there'll be a complete attitude indicator solution on this thread.