Here is a snippet of an older Game Engine using OpenGL instead of Direct X. You may have to adjust for the handedness of the coordinate system but the basic principles still apply. When working with movement in a 3D environment; the movement that either the camera, player or world objects experience should be done through a switch
statement instead of a bunch of if else
statements.
Take a look at this snippet for rotational motion done within an OpenGL Game Engine.
void Player::move( Action action, float fDeltaTime ) {
v3LookDirection = m_v3LookCenter - m_v3Position;
switch( action ) {
case MOVING_FORWARD: {
// ... code here ...
}
case MOVING_BACK: {
// ... code here ...
}
case MOVING_RIGHT: {
// ... code here ...
}
case MOVING_LEFT: {
// ... code here ...
}
case LOOKING_LEFT: {
/*float fSin = -sin( fDeltaTime * m_fAngularSpeed );
float fCos = cos( fDeltaTime * m_fAngularSpeed );
m_v3LookCenter.m_fX = m_v3Position.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3LookCenter.m_fZ = m_v3Position.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;*/
// Third Person
float fSin = sin( fDeltaTime * m_fAngularSpeed );
float fCos = -cos( fDeltaTime * m_fAngularSpeed );
m_v3Position.m_fX = m_v3LookCenter.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3Position.m_fZ = m_v3LookCenter.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;
}
case LOOKING_RIGHT: {
/*float fSin = sin( fDeltaTime * m_fAngularSpeed );
float fCos = cos( fDeltaTime * m_fAngularSpeed );
m_v3LookCenter.m_fX = m_v3Position.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3LookCenter.m_fZ = m_v3Position.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;*/
// Third Person
float fSin = -sin( fDeltaTime * m_fAngularSpeed );
float fCos = -cos( fDeltaTime * m_fAngularSpeed );
m_v3Position.m_fX = m_v3LookCenter.m_fX + (-fSin * v3LookDirection.m_fZ + fCos * v3LookDirection.m_fX );
m_v3Position.m_fZ = m_v3LookCenter.m_fZ + ( fCos * v3LookDirection.m_fZ + fSin * v3LookDirection.m_fX );
break;
}
case LOOKING_UP: {
m_v3LookCenter.m_fY -= fDeltaTime * m_fAngularSpeed * m_MouseLookState;
// Check Maximum Values
if ( m_v3LookCenter.m_fY > (m_v3Position.m_fY + m_fMaxUp ) ) {
m_v3LookCenter.m_fY = m_v3Position.m_fY + m_fMaxUp;
} else if ( m_v3LookCenter.m_fY < (m_v3Position.m_fY - m_fMaxDown) ) {
m_v3LookCenter.m_fY = m_v3Position.m_fY - m_fMaxDown;
}
break;
}
} // switch
}
Where all of the declared local and member variables that begin with m_v3...
are Vector3 objects. Vector3 objects has a x,y,z
components and all of the available math that can be done to vectors and Action is an enumerated type.
And this function is called within my Scene
class.
void Scene::playerAction( float fMouseXDelta, float fMouseYDelta ) {
if ( fMouseXDelta != 0.0f ) {
m_player.move( LOOKING_RIGHT, fMouseXDelta );
}
if ( fMouseYDelta != 0.0f ) {
m_player.move( LOOKING_UP, fMouseYDelta );
}
}
And also in Scene::update()
void Scene::update() {
UserSettings* pUserSettings = UserSettings::get();
AudioManager* pAudio = AudioManager::getAudio();
bool bPlayerMoving = false;
// Movement
if ( pUserSettings->isAction( MOVING_FORWARD ) ) {
m_player.move( MOVING_FORWARD, GameOGL::getPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( pUserSettings->isAction( MOVING_BACK ) ) {
m_player.move( MOVING_BACK, GameOGL::getPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( pUserSettings->isAction( MOVING_LEFT ) ) {
m_player.move( MOVING_LEFT, GameOGL::getPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( pUserSettings->isAction( MOVING_RIGHT ) ) {
m_player.move( MOVING_RIGHT, GameOGL::getPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( bPlayerMoving && !m_bPlayerWalking ) {
pAudio->setLooping( AUDIO_FOOTSTEPS, true );
pAudio->play( AUDIO_FOOTSTEPS );
m_bPlayerWalking = true;
}
else if ( !bPlayerMoving && m_bPlayerWalking ) {
pAudio->stop( AUDIO_FOOTSTEPS );
m_bPlayerWalking = false;
}
// Bunch more code here.
}
This is also tied into the GameOGL
class that works with the messageHandler()
that I'm not going to show here. This is coming from a medium to large scale project that consists of nearly 50k lines of code. It is to large to display every working piece here so please don't ask because everything in this engine is integrated together. I just was showing the basic math that is used for doing rotational movement, either if it is invoked by a key press or mouse movement.
Now you have to remember this because it is important. The actual calculations that you see coming from the Player
class that does the rotations you may not be able to use directly. If the handedness of the coordinate system is different than the one used here; you will have to use the appropriate trig functions to the appropriate coordinate axis members with the proper signs for the calculations to be correct. When the handedness changes so does the axis of rotation that is being implied as well as the initial direction of the rotation. Isn't 3D Math Fun?
EDIT
Oh I also noticed that you are using DirectX's
::CreateFromYawPitchRoll()
to create your rotation matrix; this is okay but you need to be careful with rotations that use standard Euler Angles. If you begin to do rotations in more than one degree of motion at the same time; you will end up experiencing Gimbal Lock. To avoid using Gimbal Lock Problems within 3D rotations; it is better to use Quaternions
The math to them is a little harder to take in, the concepts of what they are isn't too hard to understand, but using them is actually quite easy and is also very efficient on calculations. Many math libraries contain them; DirectX's math library should and so does the Open Source GLM
math library that is used widely with OpenGL & GLSL. If you are unsure of Gimbal Lock and Quaternions you can do a Google search to look up those topics; there is plenty of information out there about them. Isn't Advanced 3D... eh hem... 4D Math Fun?