Spaceship Movement

Started by
7 comments, last by bobbias 11 years, 10 months ago
Hi fellow programmers,
i'm currently working on a prototype of a space fighting game and having a hard time figuring out how the movement of the spaceships can be realised. My first approach was using several rotation and translation matrices but while i succeed in placing and moving the ship i failed miserably in changeing the direction of the objects =(. Like if the ship is upsidedown and it should rotate left it is rotating right which doesn't surprise me but i could not find a way to solve the problem. My researches brought up these "Quarternions", which certenly are a cool thing but i don't know how to work properly with them. Is there anyone here how coud give me a hint (or a code snipet) where such "six degree of freedom" movement is actually implemented? I found a code written in python but i'm using C++ and so this doesn't help that much =(

Without this technique i'd have to convert the rotation of the worldspace into the "private" space of the ship ( http://vblab.de/files/problem.png like shown here) but failed again.

Any help would be appreciated!
Advertisement
Typically you will do the "player controlled" navigation of the ship in local space, and then transform that into world space for moving the ship itself. Understanding that transformation is probably the most important part of working in 6DOF environments.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

And unfortunately this is exact what i'm unable to do =/ Currently the ship movement is based on a vector giving the yaw, pitch and roll of the spaceship in worldspace and a position vector giving the x,y and z coordinates in worldspace. Moving the ship there is not that hard, the velocity vector transformed with the rotationmatrix via D3DMatrixRotationYawPitchRoll and D3DXVectorTransformCoord added to the position vector of the spaceship and it's moving like it should.

D3DXVECTOR3 position, velocity;
float yaw, pitch, roll;
D3DXMATRIX rotationMatrix;

//Position contains .x, .y, .z of the ship;
//Velocity contains x = 0.0, y = 0.0, Z = 1.0 so the vessel will move forward
//Yaw, Pitch, Roll hold the ypw values in worldspace

// Create the rotation matrix from the yaw, pitch, and roll values.
D3DXMatrixRotationYawPitchRoll(&rotationMatrix, yaw, pitch, roll);

// Transform the velocity vector by the rotation matrix so it is correctly rotated at the origin
D3DXVec3TransformCoord(&velocity, &velocity, &rotationMatrix);

//Translate the rotated ship position to its location in worldspace
position = position + velocity;

//Store the new position of the ship


As mentioned this works fine with any values but how do i manage to change the rotation? Creating another rotationmatrix due to the user input and multiplying it to the old rotationmatrix gives odd results as much as changing the roll/pitch/yaw values since they are measured in worldspace and not localspace and google isn't much of a help here =(
I think you should change your representation for the rotations. Yaw-Pitch-Roll may be more intuitive (well, not for me actually but people seem to find it intuitive), but it's a quite bad representation when you have to compose several rotations and work with large rotations (and spaceships do rotate quite freely in the space). I suggest to either use rotation matrices or quaternions. Quaternions are probably more stable and easier to normalize, but you probably know a lot more about matrices and you may have to convert the quaternions to matrices anyway.
You simply modify the pitch, yaw, or roll values - that will change your rotation.

The tricky part is obtaining the correct Euler angles in world space. D3DX should have a function for going from an axis-angle rotation representation to Euler angles; you could use the ship's local axis-angle change during steering, transform that to world space, recover the Euler angles, and then construct your world-space transform for the new ship orientation.

Of course, it may be a hell of a lot simpler to just ditch Euler angles entirely and work in axis-angle representations always :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Hm, I thought about giving these quaternions a shot, but since DirectX eventually needs matrices to render the model I'll try to use matrices to deal with this problem. Thanks so far, I'll reply after some tests
Quaternions are best for flight motion since they allow you to travel upside down without having to keep track of your angles as much


void IMovable::accelerateForward( float scaleFactor )
{
float Matrix[16];
Quaternion q;
calculateQuats(); // calculates Quaternions from our heading and pitch into _qHeading and _qPitch
q = _qPitch * _qHeading; // Combine the 2 vectors and rotations - Order specific multiplication!
q.CreateMatrix(Matrix); // Store our new vector and its rotation
glMultMatrixf(Matrix); // This is the same as glRotatef

_qPitch.CreateMatrix(Matrix); // Load this new Matrix into _qPitch
_localVelocity.y += Matrix[9] * _acceleration * scaleFactor; // Location [9] will always contain our Y movemet
q = _qHeading * _qPitch; // Combine the Quarts again to get our X and Z vectors
q.CreateMatrix(Matrix); // Store our new vector and its rotation
_localVelocity.x += Matrix[8] * _acceleration * scaleFactor; // Location [8] will always contain our X movemet
_localVelocity.z += -Matrix[10] * _acceleration * scaleFactor; // Location [10] will always contain our Z movemet (negate Z in OpenGL)
}
void IMovable::calculateQuats()
{
_qPitch.CreateFromAxisAngle(1.0f, 0.0f, 0.0f, _pitch);
_qHeading.CreateFromAxisAngle(0.0f, 1.0f, 0.0f, _heading);
}


And in your quaternion class:


void Quaternion::CreateFromAxisAngle(float x, float y, float z, float degrees)
{
// First we want to convert the degrees to radians
// since the angle is assumed to be in radians
float angle = degreesToRadians(degrees);
// Here we calculate the sin( theta / 2) once for optimization
float result = (float)sin( angle / 2.0f );

// Calcualte the w value by cos( theta / 2 )
_w = (float)cos( angle / 2.0f );
// Calculate the x, y and z of the quaternion
_x = float(x * result);
_y = float(y * result);
_z = float(z * result);
}
void Quaternion::CreateMatrix(float *pMatrix)
{
// Make sure the matrix has allocated memory to store the rotation data
if(!pMatrix) return;
// First row
pMatrix[ 0] = 1.0f - 2.0f * ( _y * _y + _z * _z );
pMatrix[ 1] = 2.0f * (_x * _y + _z * _w);
pMatrix[ 2] = 2.0f * (_x * _z - _y * _w);
pMatrix[ 3] = 0.0f;
// Second row
pMatrix[ 4] = 2.0f * ( _x * _y - _z * _w );
pMatrix[ 5] = 1.0f - 2.0f * ( _x * _x + _z * _z );
pMatrix[ 6] = 2.0f * (_z * _y + _x * _w );
pMatrix[ 7] = 0.0f;
// Third row
pMatrix[ 8] = 2.0f * ( _x * _z + _y * _w );
pMatrix[ 9] = 2.0f * ( _y * _z - _x * _w );
pMatrix[10] = 1.0f - 2.0f * ( _x * _x + _y * _y );
pMatrix[11] = 0.0f;
// Fourth row
pMatrix[12] = 0;
pMatrix[13] = 0;
pMatrix[14] = 0;
pMatrix[15] = 1.0f;
// Now pMatrix[] is a 4x4 homogeneous matrix that can be applied to an OpenGL Matrix
}


as you receive input to change the direction you adjust q_pitch and q_heading accordingly and it will get calculated. I recommend creating a movable interface/class and deriving all your objects that need to move from it.
Will try it right away with this code. Thank you so much!
VBLab, quaternions solve a number of problems, and if you have a working implementation you can actually treat them as a black box object that just does what you need it to do. It's not hard to construct a rotation matrix on the fly form a quaternion, even by hand.

The problem that quaternions solve is http://en.wikipedia.org/wiki/Gimbal_lock which can cause your movements to end up not working correctly.

This topic is closed to new replies.

Advertisement