Issues with 3rd person space ship flight algorithm

Started by
41 comments, last by Zakwayda 13 years ago
Hey!

After a few days of trying to figure it out on my own, I am unable to figure out what is wrong or what I am not doing right in trying to make a 3rd space flight demo.

At the moment, my ship moves properly if I turn left or right or if I go up and down but when I start turning around and mixing axes, things get messed up at some point. The ship just starts moving backwards or just drifts away in a direction.

I'm using OpenGL so I'm not sure whether this belongs to the OpenGL forum but I thought this was more theory than actual OpenGL issues.

The way I compute my movement is the following :

I start with the ship at the origin and give it a direction vector going [0,0,-1]. My world is a simple sphere spanning about 25000 in radius and I intend on making everything stay inside that.

By pressing the arrow keys, I change the Heading Angle and the Pitch Angle of the ship by a small amount. Those angles return to 0 if the keys aren't pressed anymore.

Every frame, I rotate the rest of the world opposite of those angles by a ratio of 1/30 of the value of those angles. so if the ship is tilted 30 degrees upwards on that frame, I will rotate the world by 1 degree in the opposite direction to give it a slower pace. If I keep those angle 1 to 1 the world spins so fast it's impractical.

To computer proper positioning, I take my initial vector [0,0,-1] and put it inside a 4x4 matrix homogeneous matrix. I obtain the current matrix transformation from the ModelViewMatrix and multiply them together to obtain a new unit direction vector for my ship. With this unit direction vector, I update the position by adding each successive new direction vector I obtain. So far, it seems to keep track of the position very well but my direction is often reversed and I don't know what I must do.

Do you guys have a suggestion of what could be going wrong ? I can always post some code if necessary. I thought I would ask first and see if perhaps someone would have a good lead on what I should look into and try to figure out on my own in order to learn from this.

I'm probably doing lots of things wrong so don't hesitate to tell me what I should change.

Thanks!
Advertisement
I think a good first step would be to define precisely what kind of motion control scheme you want.

It sounds like only pitch and yaw can be modified (that is, no roll). Are these rotations intended to be relative, or is there a 'fixed' up vector? In other words, do you want a 6DOF control scheme, or something more like a first-person character control scheme?
From what I understand of 6DOF vs FPS style I would tend to say more of a 6 degrees of freedom approach.

At the moment, I have Yaw and Pitch implemented. Rolling is there as well but is meant to act differently. I make the ship roll over no more than 90 degrees on each side and so the world rolls on either side but follows the angle of the ship itself. when the ship's roll angle returns to 0, so does the rolling rotation applied to the world.

I'm not entirely certain what you mean by relative rotations or fixed up vector but I'll try to answer that the best I can. Logically I would believe 6DOF to be relative and FPS to be a fixed up vector. since the ship can't really move backwards and can only go forward, and where everything around the ships moves according to its movement I think I'm looking for relative rotations and translations. A fixed vector approach would make sense for an FPS but I don't know how I would apply it to my ship.

Each frame, I start assuming the ship is at [0,0,0] in the direction [0,0,-1]. Based on the world rotation made to the scene at that frame, I calculate the matrix multiplication of that matrix state with my [0,0,-1] initial direction vector to to get the proper translation vector that I use to move the world around the ship. The ship is always at [0,0,0] the entire time.

I have two sets of rotations that are independent from each other that I use together: The ship's rotation angles and the world's rotation angle. The ship angles are only used to animate the ship and indicate how much the world should rotate around the world. If I stop animating the ship and disable the rotations, I have a fixed ship, seen from the back, and a bit from above that doesn't move on its own but I the world around it rotates. From what I understand, the ship must always face the direction it is going so this is where it bugs up depending on the coordinates.

if I change the pitch angles either up or down, I end up with the world upside down and going the right way. as Long as I don't turn left or right more than 90 degrees more or less than it's still all fine. The same goes for the Yaw angle. as long as the ship is flat on the X/Z plane more or less. turning around anyway I want works fine. if I start going up or down to around 90 degrees, the direction of the ship gets reversed as well as the yaw rotations.

*Edit*
An important information would be that the camera is also always looking at [0,0,0]. I tried to move the camera weeks ago and I am not comfortable with how it works and how to move the camera in the scene. Some information I found and discussing it with a friend made me decide moving the world around might be simpler for me than to move the camera and the ship in the world. Perhaps this was a mistake?
*Edit*

I don't know what else to add so far. I will probably be able to give more pertinent details later as the discussion goes on.



Thanks!
There's a lot of different things that can go wrong with something like this. I didn't completely follow all the details of the movement scheme you described, but here's a few general tips that might be useful:

- Generally speaking, you'll want to do all the math yourself (using your own code or a third-party math library) rather than using OpenGL convenience functions or accessing the modelview matrix directly. This will give you more control and will provide cleaner interaction with the OpenGL API.

- For 6DOF motion, the general approach is to store the object's orientation as a matrix or quaternion, apply local, incremental rotations as necessary each update, normalize the orientation to prevent numerical drift, and then build a transform matrix from the orientation and position for uploading to OpenGL (or whatever API you're using). The direction vectors for the object can then be extracted directly from the matrix for the purpose of applying linear motion.

- Unless you have a specific reason for doing so, I wouldn't think of (or implement) the ship's motion in terms of applying opposite rotations to the world. (Again, unless you're going for a specific effect or have a specific need to do it that way.) Just let the world be static and move the ship using normal methods; once you've built the ship's transform matrix, you can then invert it to yield the view transform.

There's a lot of different things that can go wrong with something like this. I didn't completely follow all the details of the movement scheme you described, but here's a few general tips that might be useful:

- Generally speaking, you'll want to do all the math yourself (using your own code or a third-party math library) rather than using OpenGL convenience functions or accessing the modelview matrix directly. This will give you more control and will provide cleaner interaction with the OpenGL API.

- For 6DOF motion, the general approach is to store the object's orientation as a matrix or quaternion, apply local, incremental rotations as necessary each update, normalize the orientation to prevent numerical drift, and then build a transform matrix from the orientation and position for uploading to OpenGL (or whatever API you're using). The direction vectors for the object can then be extracted directly from the matrix for the purpose of applying linear motion.

- Unless you have a specific reason for doing so, I wouldn't think of (or implement) the ship's motion in terms of applying opposite rotations to the world. (Again, unless you're going for a specific effect or have a specific need to do it that way.) Just let the world be static and move the ship using normal methods; once you've built the ship's transform matrix, you can then invert it to yield the view transform.


I'm sure what I wrote isn't exactly easy to follow. I already have a bit of my own matrix operations going here and there. Using the Model View Matrix of OpenGL was an idea I got based on loading an identity matrix into it and then doing operations on it to obtain the transformations I wanted and applying them to my direction vector to get another matrix from which I could extract the properly transformed vector.

I agree with you that this may not be a good idea. This was mostly to save time as I noticed it always gave me the results I expected and decided to use it. I'll try to apply my own matrix transformations. I'm just hoping I won't screw up the functions. Using a third party would probably be more powerful but as I'm trying to learn how it works from the ground up I think going the painful way might be better.

I'm just not sure about normalization. I know what it is, but I'm not sure if I need to normalize if the vector I provide my operations with a unit vector to start with. will that vector need normalizing after transformation or will it remain in unit form ?

I will also attempt moving the camera and reporting. There are other kinds of operations I am not sure how to go about it. I'll post more as I go along. I'm not entirely certain how I would go about it but I'm going to use your advice as a base for what to do.

I'll post an update as I go on.

Thanks!
Regarding the normalization I mentioned, the purpose of that is to correct for numerical error caused by multiple successive incremental rotations.

Due to the imprecision of floating-point representations, rotations in both quaternion and matrix form will generally drift over time, and at some point noticeable artifacts may appear (e.g. scaling with quaternions, or scaling and/or shear with matrices). Normalizing (in the general sense) the rotation is intended to correct this. With quaternions, this equates to a quaternion normalization, which is a simple and fairly efficient operation. With matrices it requires orthogonalization, which is a bit less simple and efficient, but is still relatively straightforward.
I had the same issue with my space simulator. My camera would use the y axis as the "up" vector, effectively locking it to a first person camera. The trick is to use Quaternions. It's been a year or so since I implemented it, but I believe I created a quaternions for the yaw, pitch and roll angles, and then concatenated them together, and then converted it to a matrix. The result is a camera similar to the old Star Wars X-Wing games. Using matrices causes issues with the gimbol lock and having to use an up vector (I'm not sure if there's a solution to this, but I never found it). But yeah, look into that.

*Edit* I reread the title and noticed the "3rd person" part. My solution was for a first person camera. Still, I believe quaternions are your best bet.

I had the same issue with my space simulator. My camera would use the y axis as the "up" vector, effectively locking it to a first person camera. The trick is to use Quaternions. It's been a year or so since I implemented it, but I believe I created a quaternions for the yaw, pitch and roll angles, and then concatenated them together.

A couple comments on the above (just to prevent confusion).

First of all, whether you use quaternions or matrices makes no difference as far as gimbal lock or anything like that goes. Generally speaking, you can get the same results regardless of whether you use quaternions or matrices. Each representation has its own advantages and disadvantages, but their fundamental behavior with respect to rotations is equivalent. (Just to be clear, I'm not talking about memory requirements, efficiency of specific operations, stability, or any of the other areas where quaternions and matrices do indeed differ; rather, I'm talking about fundamental behaviors, such as whether or not gimbal lock can occur and how rotations can be constructed and manipulated.)

Second, creating separate yaw, pitch, and roll quaternions is the same as constructing an Euler-angle rotation using any other method; the use of quaternions here makes no difference and doesn't buy you anything in particular.

Using matrices causes issues with the gimbol lock and having to use an up vector (I'm not sure if there's a solution to this, but I never found it). [/quote]
Gimbal lock and whether you have to use an 'up' vector have nothing to do with whether quaternions or matrices are used. (There's a lot of confusion about these topics already, so I think it's worth clarifying these points when they come up, which seems to be fairly often.)
This kind of topic might be a good candidate for a sticky.

My opionion is simple on this: take those Euler Angles behind the barn and put them out of their misery. Three angles can't store the information you need, which is not just three isolated summed up angles, but the _order_ in which they were applied. Yes, you can easily get away in a scenario with limited motion (1st person shooter), where there is always one "correct" order to apply them (usually y-axis, x-axis, z-axis).

Just use matrices in the first place and forget all the trigonometry you apply to those angles (and all the convoluted tricks to somehow hammer Euler Angles into working).
Matrices are neat and simple, they even tell you your ships forward axis (AND right AND up AND position) in 4 easy to read vectors. You will eventually need a matrix library anyway, so either find one you like or quickly write one yourself (even my straightforward code was automatically compiled to make heavy use of SSE).

So next time dont do
"yaw += 2", do
"transformation = Matrix::rotationMatrixY(2) * transformation;

even saves you the annoying step of converting your angles back to a matrix (or rather, one of many different possible matrices of which in a 6DOF scenarion most likely none will be the right one).


About quaternions. I got a feeling especially those that never quite got the math love to suggest it as a magic bullet for a problem they didn't fully understand. Jyk said pretty much all about that and I doubt you will find them overly useful (or better performing) until you start adding skeletal animations or other features that require a lot of concatenations. After all, you have to eventually transform them back to matrices for your API and the speed up in calculations has to make up for that overhead.
f@dzhttp://festini.device-zero.de
I have to admit, I am completely lost...

Moving the ship instead of the world also requires me to move the camera to keep the view the same. The only way I know how to control a camera view is through gluLookAt() but so far all attempts have failed at getting where I want to be. I have a feeling I have to control the eye and Center value manually but I have no clue how to update it properly each frame. The only thing that comes to mind is using the eye as the position and provide a direction unit vector to the center. And since the camera is backed away from the ship, I also need to translate the position opposite of the center and then raise it a bit. This is extremely confusing.

I realized I can achieve a similar outcome rotating and translating the projection view matrix so this is what I am attempting right now without much success. I can get the camera to follow the ship properly by translating it first and rotating it by the opposite rotation that I do for the ship and it works fine on a 2D plane. The ship only rotates at the origin at the moment and does not move.

The issue I have is when I attempt to rotate in 3D, the axes get all scrambled. If I turn the left 90 degrees and attempt to go up, instead of rotating in that direction, the view is tilted left or right. I understand why it happens but I'm looking for a way to make the axes "follow" along with the ship in a way.

Once I get that sorted out I will be able to try moving the ship in the space.

Also, I tried to create a matrix that I keep updating frame after frame but it goes awfully wrong when I use rotation and translation. because the matrix contains both rotation and translations into it, when I add further translations and rotations it becomes Screwed up beyond all recognition. Should I keep two separate matrices and update them each frame and put them together only when comes the time to update the view?

I think I'm going to need some step by step guidance. I get too confused with everything.

This topic is closed to new replies.

Advertisement