Hello,
I have a question on how to implement physics.
My current physics:
I have a CMotion class that accepts motion vectors. In my message loop,
I call the ApplyMotion(dwTimeElapsedSinceLastCall); function of this
class. Since all my motion behavior (like speed, acceleration) are defined
for one second, my class has to look how many time has elapsed and then
scale the things to this time. Things like gravity and gravity acceleration
are also applied from this class.
This works very well, but I have problems to convert keyboard & mouse input
to vectors for my class. If I pass a forward vector to my class every time
my HandleKeyboard() function realizes the forward key is pressed, my acceleration
would depend on how often this function could be called. Bad. So I have to
scale this too. I call something like AddForwardVector(dwTimeElapsedSinceLastCall);
and the forward vector is so scaled that the whole acceleration is applied when
I hold my forward key a full second down, regardless of how often HandleKeyboard()
is called.
My problem:
This method requires for all forces to be applied over the time. I want to let the player
jump, but to jump you shouldn’t have to hold the mouse button down like walking. But when
I call my Jump(); it adds a vector to my CMotion class which will be smoothed out quickly
by the other ones.
Here is my source (sorry):
CMotion::CMotion()
{
// Init motion behavior with standard values. Every values is meant
// to be applied in 1000ms
m_SlowDown = 7.5f;
m_MidAirSlowDown = 0.1f;
m_GravityAcceleration = 2.5;
m_Gravity[x] = 0.0f;
m_Gravity[y] = -0.15f;
m_Gravity[z] = 0.0f;
// Init other member variables
m_MidAir = FALSE;
m_SmallestForce = 0.01f;
m_AccelerationTime = 100;
m_MidAirAccelerationTime = 500;
m_JumpForce[x] = 0.0f;
m_JumpForce[y] = 1.0f;
m_JumpForce[z] = 0.0f;
m_CurrentGravity[x] = 0.0f;
m_CurrentGravity[y] = 0.0f;
m_CurrentGravity[z] = 0.0f;
}
CMotion::~CMotion()
{
}
void CMotion::ApplyMotionPhysics(DWORD dwTimeElapsed)
{
// Calculate the movement that would happen in dwTimeElapsed ms
// Copy the current motion vector to cFinalForce
CForce cFinalForce = m_Force;
// Is camera in the air ?
CheckMidAir();
// Apply gravity to the final movement vector
if (m_MidAir)
{
// Camera is diving, apply gravity acelleration
m_CurrentGravity[x] += ScaleToElapsedTime(m_CurrentGravity[x] * m_GravityAcceleration, dwTimeElapsed);
m_CurrentGravity[y] += ScaleToElapsedTime(m_CurrentGravity[y] * m_GravityAcceleration, dwTimeElapsed);
m_CurrentGravity[z] += ScaleToElapsedTime(m_CurrentGravity[z] * m_GravityAcceleration, dwTimeElapsed);
// Apply gravity
cFinalForce += m_CurrentGravity;
}
else
// Camera is on the ground, set current gravity to default gravity
m_CurrentGravity = m_Gravity;
// Scale the final motion vector relative to dwTimeElapsed
cFinalForce[x] = ScaleToElapsedTime(cFinalForce[x], dwTimeElapsed);
cFinalForce[y] = ScaleToElapsedTime(cFinalForce[y], dwTimeElapsed);
cFinalForce[z] = ScaleToElapsedTime(cFinalForce[z], dwTimeElapsed);
// Pass the position after the motion to the CCamera class
SetXPos(GetXPos() + cFinalForce[x]);
SetYPos(GetYPos() + cFinalForce[y]);
SetZPos(GetZPos() + cFinalForce[z]);
// Lift the camera up to the terrain if he has descended below it
float fHeight = GetSurfaceHeight();
if (GetYPos() < fHeight)
{
// Lift camera up
SetYPos(fHeight);
// No acceleration upwards
m_Force[y] = 0.0f;
}
// Slow the motion down
// Use m_MidAirSlowDown when camera is in the air
if (m_MidAir)
// Reduce force as specified in m_MidAirSlowDown. Scale to the elapsed time
m_Force.SlowDown(ScaleToElapsedTime(m_MidAirSlowDown, dwTimeElapsed));
else
// Reduce force as specified in m_SlowDown. Scale to the elapsed time
m_Force.SlowDown(ScaleToElapsedTime(m_SlowDown, dwTimeElapsed));
// Set it to zero if it is to small
if (fabs(m_Force[x]) + fabs(m_Force[y]) +
fabs(m_Force[z]) < m_SmallestForce)
m_Force.ResetForce();
}
void CMotion::SetCustomMotion(float fForwardSpeed, float fBackwardSpeed, float fLeftSpeed, float fRightSpeed,
float fUpSpeed, float fDownSpeed, DWORD dwTimeElapsed)
{
// Add the given forces to the current force. Do scale the given forces relative to the
// elapsed time. Do not exceed maximum speed.
// Holds the new motion
CForce cAddForce;
// Calculate the given forces
if (fForwardSpeed > 0.0f)
{
CForce cForward;
cForward[x] = -(sinf(GetYRotation() * PI_OVER_180) * fForwardSpeed);
cForward[y] = 0.0f;
cForward[z] = -(cosf(GetYRotation() * PI_OVER_180) * fForwardSpeed);
cAddForce += cForward;
}
if (fBackwardSpeed > 0.0f)
{
CForce cBackward;
cBackward[x] = sinf(GetYRotation() * PI_OVER_180) * fBackwardSpeed;
cBackward[y] = 0.0f;
cBackward[z] = cosf(GetYRotation() * PI_OVER_180) * fBackwardSpeed;
cAddForce += cBackward;
}
if (fLeftSpeed > 0.0f)
{
CForce cLeft;
cLeft[x] = sinf((GetYRotation() - 90.0f) * PI_OVER_180) * fLeftSpeed;
cLeft[y] = 0.0f;
cLeft[z] = cosf((GetYRotation() - 90.0f) * PI_OVER_180) * fLeftSpeed;
cAddForce += cLeft;
}
if (fRightSpeed > 0.0f)
{
CForce cRight;
cRight[x] = sinf((GetYRotation() + 90.0f) * PI_OVER_180) * fRightSpeed;
cRight[y] = 0.0f;
cRight[z] = cosf((GetYRotation() + 90.0f) * PI_OVER_180) * fRightSpeed;
cAddForce += cRight;
}
if (fUpSpeed > 0.0f)
{
CForce cUp;
cUp[x] = 0.0f;
cUp[y] = fUpSpeed;
cUp[z] = 0.0f;
cAddForce += cUp;
}
if (fDownSpeed > 0.0f)
{
CForce cDown;
cDown[x] = 0.0f;
cDown[y] = -fDownSpeed;
cDown[z] = 0.0f;
cAddForce += cDown;
}
// Is camera in the air ?
CheckMidAir();
// Scale length of additional force vector relative to the acceleration time
if (m_MidAir)
cAddForce.SetVectorLength((GetSpeed() / m_MidAirAccelerationTime) * dwTimeElapsed);
else
cAddForce.SetVectorLength((GetSpeed() / m_AccelerationTime) * dwTimeElapsed);
// Add the additional force vector to the current force
m_Force += cAddForce;
// Do not exceed maximum speed
m_Force.SetVectorLength(GetSpeed());
}
void CMotion::SetDefaultMotion(bool bForward, bool bBackward, bool bLeft, bool bRight,
bool bUp, bool bDown, DWORD dwTimeElapsed)
{
// Add the enabled motion vectors with default accelerations to the array
// Convert parameters for SetCustomMotion() and pass them
SetCustomMotion(bForward * GetSpeed(), bBackward * GetSpeed(),
bLeft * GetSpeed(), bRight * GetSpeed(),
bUp * GetSpeed(), bDown * GetSpeed(), dwTimeElapsed);
}
void CMotion::Jump()
{
// Add jump acceleration to there current force if player
// is on the ground
CheckMidAir();
if (!m_MidAir)
m_Force += m_JumpForce;
}
void CMotion::CheckMidAir()
{
// Checks if the camera is in the air
// Use offset to avoid calculation accurancy problems
if (GetYPos() > GetSurfaceHeight() + 0.0000001f)
m_MidAir = TRUE;
else
m_MidAir = FALSE;
}
Any ideas ? I think my physics code is good, but my way to keep the motion vector
in sync with the mouse + keyboard input is bad…
Any ideas would be great !!!
Hope my English was good enough to make myself clear
(Sorry again for being off-topic. If you don’t want to reply on the board, please
mail me)
Tim