Quaternion Rotation based on Forward Vector and/or Velocity
OK, I'm trying to rotate an object based on it's forward vector. This object is AI controlled, so all that I really have to work with is a forward vector, Velocity, and Acceleration. Anything else I need I can generate no problem, but this is has slipped my mind.
My player object rotates in a ProcessKeyboard function, see the snippet below:
Quaternion additionalRot = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, -1), leftRightRot) * Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), upDownRot);
Rotation *= additionalRot;
(Based off of code found at http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2/Flight_kinematics.php)
I figured maybe this could give a little insight on what to do.
Is that c#?
use the forward vector to generate an orhtogonal basis for your object (http://www.gamedev.net/community/forums/topic.asp?topic_id=544271 nice example there). Then rotate as you are doing with your player position. Replace Vector(0, 0, -1) by the "up" vector and Vector3(1, 0, 0) by the "right" vector.
use the forward vector to generate an orhtogonal basis for your object (http://www.gamedev.net/community/forums/topic.asp?topic_id=544271 nice example there). Then rotate as you are doing with your player position. Replace Vector(0, 0, -1) by the "up" vector and Vector3(1, 0, 0) by the "right" vector.
Yes it's in C# using the xna framework
Now I probably should have mentioned this before, the two variables leftRightRot and upDownRot where values that were modified by input in the ProcessKeyboard function, like so:
float leftRightRot = 0;
float upDownRot = 0;
float turningSpeed = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
turningSpeed *= 1.6f * gameSpeed;
KeyboardState keys = Keyboard.GetState();
if (keys.IsKeyDown(Keys.Right))
leftRightRot += turningSpeed;
if (keys.IsKeyDown(Keys.Left))
leftRightRot -= turningSpeed;
if (keys.IsKeyDown(Keys.Down))
upDownRot += turningSpeed;
if (keys.IsKeyDown(Keys.Up))
upDownRot -= turningSpeed;
But, hey! We're on the right track!
Now I probably should have mentioned this before, the two variables leftRightRot and upDownRot where values that were modified by input in the ProcessKeyboard function, like so:
float leftRightRot = 0;
float upDownRot = 0;
float turningSpeed = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
turningSpeed *= 1.6f * gameSpeed;
KeyboardState keys = Keyboard.GetState();
if (keys.IsKeyDown(Keys.Right))
leftRightRot += turningSpeed;
if (keys.IsKeyDown(Keys.Left))
leftRightRot -= turningSpeed;
if (keys.IsKeyDown(Keys.Down))
upDownRot += turningSpeed;
if (keys.IsKeyDown(Keys.Up))
upDownRot -= turningSpeed;
But, hey! We're on the right track!
Well what rotates your objects? will they rotate to something? such as "look at the player" or will they have some form of behaviour?
To give you some ideas, at the moment I am using behaviours (seek, intercept, way poitn follow etc). Objects accelerate in their forward direction. Behaviours just steers. Somehting like seeking (heading straight to a point):
Work out direction thats needed to go, target - position. Create a "desired" rotation (the orientation the entity wishes to be in to move forward to the point) and then interpolate between its current rotation and its desired rotation.
I'm not really sure the best way to do this so don't take this as Gospel but what I did was store 2 quaternions, start point and end point (desired) and each time I update my entity I also update the rotation using SLERP.
I put the behaviour in a simple object:
When I want some entity to rotate I do:
m_RotateObject = new Rotation(m_Rotation, target, m_AngularVelocity);
and inside my update method I check if m_RotateObject exists, if it does I update it. If its finished rotating I delete the rotation object.
As I said, I don't really know how it should be done but I think its a fairly neat solution and somehting to think about.
I was really not sure about angular velocity but what I have there is: if my entity can turn around (180 degrees) in 20 seconds then I set speed as 1/20.
Another option is to just accelerate int he direction you want to go. Then you allign the entity with its current velocity (use velocity to calculate a basis then load your quaternion from that matrix). With that option you don't have to do any rotating at all, it just sorta works itself out.
To give you some ideas, at the moment I am using behaviours (seek, intercept, way poitn follow etc). Objects accelerate in their forward direction. Behaviours just steers. Somehting like seeking (heading straight to a point):
Work out direction thats needed to go, target - position. Create a "desired" rotation (the orientation the entity wishes to be in to move forward to the point) and then interpolate between its current rotation and its desired rotation.
I'm not really sure the best way to do this so don't take this as Gospel but what I did was store 2 quaternions, start point and end point (desired) and each time I update my entity I also update the rotation using SLERP.
I put the behaviour in a simple object:
class Rotation{public: Rotation(const Engine::Quaternion &start, const Engine::Quaternion &end, float vel); ~Rotation(void); // returns true on completion of the rotation // puts current value in out if requested bool Update(unsigned int miliseconds, Engine::Quaternion *out); Engine::Quaternion GetCurrent() const { return m_Current; }private: float m_AngularSpeed; Engine::Quaternion m_Start; Engine::Quaternion m_End; Engine::Quaternion m_Current; float m_Dist; float m_Time;};Rotation::Rotation(const Engine::Quaternion &start, const Engine::Quaternion &end, float vel){ m_AngularSpeed = vel; if(m_AngularSpeed < 0) { m_AngularSpeed = 0; } m_Start = start; m_End = end; m_Current = m_Start; m_Time = 0; float d = m_Start.DotProduct(m_End); m_Dist = acos(d)/1.570796327f; // 90 degrees in radians}bool Rotation::Update(unsigned int miliseconds, Engine::Quaternion *out){ float seconds = miliseconds/1000.0f; float amount = seconds*m_AngularSpeed/m_Dist; m_Time += amount; if(m_Time > 1) { m_Time = 1; } m_Current = Engine::Quaternion::Slerp(m_Start, m_End, m_Time); if(NULL != out) { *out = m_Current; } return m_Time == 1;}
When I want some entity to rotate I do:
m_RotateObject = new Rotation(m_Rotation, target, m_AngularVelocity);
and inside my update method I check if m_RotateObject exists, if it does I update it. If its finished rotating I delete the rotation object.
As I said, I don't really know how it should be done but I think its a fairly neat solution and somehting to think about.
I was really not sure about angular velocity but what I have there is: if my entity can turn around (180 degrees) in 20 seconds then I set speed as 1/20.
Another option is to just accelerate int he direction you want to go. Then you allign the entity with its current velocity (use velocity to calculate a basis then load your quaternion from that matrix). With that option you don't have to do any rotating at all, it just sorta works itself out.
The object I'm rotating have seek and pursuit behaviors. The seek calculates acceleration which is used to find the velocity which is added to the position yadda yadda yadda...
I did like the interpolate idea, but I'm not too sure I can use that it can really be used for what I'm trying to use it for. I'm just trying to rotate them basically in the direction the velocity is going in (which would be towards the target).
Me and a buddy worked on it breifly, we tried finding the angle between the velocity and the forward vector and use that angle in Quaternion.CreateFromAxisAngle(), But the problem is we don't know which axis that would be modifying :P
I did like the interpolate idea, but I'm not too sure I can use that it can really be used for what I'm trying to use it for. I'm just trying to rotate them basically in the direction the velocity is going in (which would be towards the target).
Me and a buddy worked on it breifly, we tried finding the angle between the velocity and the forward vector and use that angle in Quaternion.CreateFromAxisAngle(), But the problem is we don't know which axis that would be modifying :P
I did seeking/intercepting behaviour purely with acceleration and no rotation. Just accelerate your object as normal then every so often align the object with its velocity.
That is, set forward == normalized _elocity, calculate right and up vectors from that. You can get a matrix from that and a quaternion from that matrix. My code if it helps:
That is, set forward == normalized _elocity, calculate right and up vectors from that. You can get a matrix from that and a quaternion from that matrix. My code if it helps:
bool MoveableBase::AlignWithVelocity(){ boost::lock_guard<boost::recursive_mutex> g(*m_Lock); float squaredSpeed = m_Velocity.SquaredLength(); if(squaredSpeed == 0) { return false; } m_Forward = m_Velocity; m_Forward.Normalize(); m_Up.Set(0, 1, 0, 0); m_Right = m_Up.CrossProduct(m_Forward); m_Up = m_Forward.CrossProduct(m_Right); m_Transformation.LoadFromBasis(m_Right, m_Up, m_Forward); m_Rotation.FromMatrix(m_Transformation); m_Rotation = m_Rotation.Conjugate(); FlagTransformChanged(); return true;}
Rotate about the binormal to the curve your aircraft is following. The binormal is the cross product of the tangent vector (normalized velocity) and the normal vector (normalized acceleration). See the Frenet-Serret page on Wikipedia.
First Nanoha, I really want to thank you for taking the time to help me and being patient with me. You rock.
Now, back to the code, I've tried several different algorithms, even the one you just posted, and they all have the same reaction: The enemies kind of pivot forward and back when they change directions, and are kind of tilted to the side. They don't really turn to face the direction they're going. I've noticed some evening moving backwards.
I'm starting to suspect that perhaps my steering behaviors might be messing with something, but if that was the case the enemies would just move in any direction.
EDIT: To Emergent, my enemies aren't really following any specific cure it's a really basic steering algorithm that pretty much goes as 1) Go to Target, 2) Pass Target, 3) If passed target, reverse, 4) repeat
Now, back to the code, I've tried several different algorithms, even the one you just posted, and they all have the same reaction: The enemies kind of pivot forward and back when they change directions, and are kind of tilted to the side. They don't really turn to face the direction they're going. I've noticed some evening moving backwards.
I'm starting to suspect that perhaps my steering behaviors might be messing with something, but if that was the case the enemies would just move in any direction.
EDIT: To Emergent, my enemies aren't really following any specific cure it's a really basic steering algorithm that pretty much goes as 1) Go to Target, 2) Pass Target, 3) If passed target, reverse, 4) repeat
Hmm both of the methods I posted work for me so perhaps the problem does lie else where. I'm not sure about the pivoting forward and back, that sounds very strange. Only thing I can think of right now is perhaps things aren't being normalized.
Make sure you normalize things that need to be normalized (basis vectors, orientation quaternion etc).
Are you using XNA? if so are these classes (quaterion in particular) fromt he framework or your own?
Make sure you normalize things that need to be normalized (basis vectors, orientation quaternion etc).
Are you using XNA? if so are these classes (quaterion in particular) fromt he framework or your own?
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement