OT: Physics, need help!

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

calculate all your physics with respect to an independant time variable, perhaps based on the system clock, so acceleration would be based on time, not the number of times the function is called.

I think that you should treat input as a set of states, related to the controlling devices. I mean, in each game loop, you read the input values and update your internal states. You’d then use stored information during the physics calculations, based on a delta time. I.e. keys can be pressed or depressed, not too much difficult to handle. For actions, like jumping, you should use a FSM, and switch states on appropriate events from the input state. This should be a fine solution, it lets you to implement acceleration in the FSM state handler.
Combine this, with a function modulating acceleration over time, and you should get smooth reactions.

I think you don’t understand my problem. If you look at my SetCustomMotion function, which is called from my keyboard handler, you’ll notice that it takes a dwElapsedTime parameter, so that the acceleration don’t depends on how often the engine could check the keyboard. But I have problems to keep acceleration with continius keydown’s ,like moving forward, in sync with single keystrokes like jumping…

Tim