Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
Fix all(?) vehicle handling problems at high FPS. Refactor FIX_BUGS i…
…n ProcessWheel() to be much simpler.
  • Loading branch information
Veyrdite committed Jul 23, 2021
1 parent 879280b commit ba832f93e80fdff79556b8e5efd4ee088e8f5a18
Showing with 85 additions and 51 deletions.
  1. +27 −0 src/entities/Physical.cpp
  2. +9 −7 src/vehicles/Automobile.cpp
  3. +5 −0 src/vehicles/Transmission.cpp
  4. +44 −44 src/vehicles/Vehicle.cpp
@@ -590,7 +590,12 @@ CPhysical::ApplyAirResistance(void)
}else if(GetStatus() != STATUS_GHOST){ }else if(GetStatus() != STATUS_GHOST){
float f = Pow(1.0f/Abs(1.0f + m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr()), CTimer::GetTimeStep()); float f = Pow(1.0f/Abs(1.0f + m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr()), CTimer::GetTimeStep());
m_vecMoveSpeed *= f; m_vecMoveSpeed *= f;
#ifdef FIX_BUGS
// Fix too much friction at high FPS (evil cause of bad vehicle handling, rear tires unable to lose traction, etc!)
m_vecTurnSpeed *= Pow(0.99f, CTimer::GetTimeStepFix());
#else
m_vecTurnSpeed *= 0.99f; m_vecTurnSpeed *= 0.99f;
#endif
} }
} }


@@ -1071,6 +1076,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){ if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseA < -impulseLimit) impulseA = -impulseLimit;
#ifdef FIX_BUGS #ifdef FIX_BUGS
@@ -1107,6 +1116,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){ if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
impulseB = (speedSum - fOtherSpeedB) * massB; impulseB = (speedSum - fOtherSpeedB) * massB;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit;
@@ -1140,6 +1153,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){ if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * massA; impulseA = (speedSum - fOtherSpeedA) * massA;
impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit;
@@ -1174,6 +1191,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){ if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * massA; impulseA = (speedSum - fOtherSpeedA) * massA;
impulseB = (speedSum - fOtherSpeedB) * massB; impulseB = (speedSum - fOtherSpeedB) * massB;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit;
@@ -1214,6 +1235,9 @@ CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint)
// not really impulse but speed // not really impulse but speed
// maybe use ApplyFrictionMoveForce instead? // maybe use ApplyFrictionMoveForce instead?
fImpulse = -fOtherSpeed; fImpulse = -fOtherSpeed;
#ifdef FIX_BUGS
fImpulse *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass; impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass;
if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
CVector vImpulse = frictionDir*fImpulse; CVector vImpulse = frictionDir*fImpulse;
@@ -1235,6 +1259,9 @@ CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint)
frictionDir = vOtherSpeed * (1.0f/fOtherSpeed); frictionDir = vOtherSpeed * (1.0f/fOtherSpeed);
#endif #endif
fImpulse = -fOtherSpeed * m_fMass; fImpulse = -fOtherSpeed * m_fMass;
#ifdef FIX_BUGS
fImpulse *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5; impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5;
if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
ApplyFrictionMoveForce(frictionDir*fImpulse); ApplyFrictionMoveForce(frictionDir*fImpulse);
@@ -857,8 +857,12 @@ CAutomobile::ProcessControl(void)
(m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f)) (m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f))
ApplyTurnForce(-GRAVITY*Min(m_fTurnMass, 2500.0f)*GetUp(), -1.0f*GetForward()); ApplyTurnForce(-GRAVITY*Min(m_fTurnMass, 2500.0f)*GetUp(), -1.0f*GetForward());
} }

#ifdef FIX_BUGS
// Keep brake non-timestepped (so the later ProcessWheel() takes only non-timestepped inputs)
brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetDefaultTimeStep();
#else
brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep();
#endif
bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING); bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING);
float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias;
float brakeBiasRear = neutralHandling ? 1.0f : 2.0f-pHandling->fBrakeBias; // looks like a bug, but it was correct in III... float brakeBiasRear = neutralHandling ? 1.0f : 2.0f-pHandling->fBrakeBias; // looks like a bug, but it was correct in III...
@@ -1058,12 +1062,7 @@ CAutomobile::ProcessControl(void)
float rearBrake = brake; float rearBrake = brake;
float rearTraction = traction; float rearTraction = traction;
if(bIsHandbrakeOn){ if(bIsHandbrakeOn){
#ifdef FIX_BUGS
// Not sure if this is needed, but brake usually has timestep as a factor
rearBrake = 20000.0f * CTimer::GetTimeStepFix();
#else
rearBrake = 20000.0f; rearBrake = 20000.0f;
#endif
if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){ if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){
m_fTireTemperature += 0.005*CTimer::GetTimeStep(); m_fTireTemperature += 0.005*CTimer::GetTimeStep();
if(m_fTireTemperature > 2.0f) if(m_fTireTemperature > 2.0f)
@@ -1072,8 +1071,11 @@ CAutomobile::ProcessControl(void)
}else if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)){ }else if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)){
rearBrake = 0.0f; rearBrake = 0.0f;
rearTraction = 0.0f; rearTraction = 0.0f;
// BUG: missing timestep #ifdef FIX_BUGS
ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStepFix());
#else
ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()); ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight());
#endif
}else if(m_fTireTemperature > 1.0f){ }else if(m_fTireTemperature > 1.0f){
rearTraction *= m_fTireTemperature; rearTraction *= m_fTireTemperature;
} }
@@ -125,7 +125,12 @@ cTransmission::CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, fl
float targetVelocity = Gears[gear].fMaxVelocity*speedMul*fCheat; float targetVelocity = Gears[gear].fMaxVelocity*speedMul*fCheat;
float accel = (targetVelocity - fVelocity) * (fEngineAcceleration*accelMul) / Abs(targetVelocity); float accel = (targetVelocity - fVelocity) * (fEngineAcceleration*accelMul) / Abs(targetVelocity);
if(Abs(fVelocity) < Abs(Gears[gear].fMaxVelocity*fCheat)) if(Abs(fVelocity) < Abs(Gears[gear].fMaxVelocity*fCheat))
#ifdef FIX_BUGS
// The acceleration provided by a transmission+engine should not depend on framelength.
fAcceleration = gasPedal * accel;
#else
fAcceleration = gasPedal * accel * CTimer::GetTimeStep(); fAcceleration = gasPedal * accel * CTimer::GetTimeStep();
#endif
else else
fAcceleration = 0.0f; fAcceleration = 0.0f;
return fAcceleration; return fAcceleration;
@@ -788,7 +788,14 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
bAlreadySkidding = false; bAlreadySkidding = false;
#endif #endif


// how much force we want to apply in these axes // Velocity impulses fwd and right. Units are like meters per second.
// This function was written assuming a fixed FPS, so a correction is made
// right at the end before actually applying these impulses.
//
// Note that many functions in this engine deal with "force impulses" rather
// than "velocity impulses". They are directly related: F=ma. It is
// possible that the original devs actually used force impulses here but
// an optimising compiler re-arranged their maths.
float fwd = 0.0f; float fwd = 0.0f;
float right = 0.0f; float right = 0.0f;


@@ -804,36 +811,30 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
bAlreadySkidding = true; bAlreadySkidding = true;
*wheelState = WHEEL_STATE_NORMAL; *wheelState = WHEEL_STATE_NORMAL;


#ifdef FIX_BUGS
// Everything else here is timestep independent, let's stay with this theme to keep the rest of the FPS bugfixes simpler.
adhesion *= CTimer::GetDefaultTimeStep();
#else
adhesion *= CTimer::GetTimeStep(); adhesion *= CTimer::GetTimeStep();
#endif
if(bAlreadySkidding) if(bAlreadySkidding)
adhesion *= pHandling->fTractionLoss; adhesion *= pHandling->fTractionLoss;


// moving sideways // moving sideways
if(contactSpeedRight != 0.0f){ if(contactSpeedRight != 0.0f){
// exert opposing force // exert opposing force
right = -contactSpeedRight/wheelsOnGround; right = -contactSpeedRight/wheelsOnGround;
// BUG?
// contactSpeedRight is independent of framerate but right has timestep as a factor
// so we probably have to fix this
// fixing this causes jittery cars at 15fps, and causes the car to move backwards slowly at 18fps
// at 19fps, the effects are gone ...
//right *= CTimer::GetTimeStepFix();


if(wheelStatus == WHEEL_STATUS_BURST){ if(wheelStatus == WHEEL_STATUS_BURST){
float fwdspeed = Min(contactSpeedFwd, fBurstSpeedMax); float fwdspeed = Min(contactSpeedFwd, fBurstSpeedMax);
#ifdef FIX_BUGS
// Keep the effect running at the same frequency even when the game is at high FPS
right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstTyreMod, fBurstTyreMod) * CTimer::GetLogicalFramesPassed();
#else
right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstTyreMod, fBurstTyreMod); right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstTyreMod, fBurstTyreMod);
#endif
} }
} }


if(bDriving){ if(bDriving){
fwd = thrust; fwd = thrust;


// limit sideways force (why?) // Limit sideways forces applied by the tires to the max the tires can possibly do.
if(right > 0.0f){ if(right > 0.0f){
if(right > adhesion) if(right > adhesion)
right = adhesion; right = adhesion;
@@ -843,11 +844,6 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
} }
}else if(contactSpeedFwd != 0.0f){ }else if(contactSpeedFwd != 0.0f){
fwd = -contactSpeedFwd/wheelsOnGround; fwd = -contactSpeedFwd/wheelsOnGround;
#ifdef FIX_BUGS
// contactSpeedFwd is independent of framerate but fwd has timestep as a factor
// so we probably have to fix this
fwd *= CTimer::GetTimeStepFix();
#endif


if(!bBraking){ if(!bBraking){
if(m_fGasPedal < 0.01f){ if(m_fGasPedal < 0.01f){
@@ -859,9 +855,6 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass; brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass;
else else
brake = mod_HandlingManager.fWheelFriction / pHandling->fMass; brake = mod_HandlingManager.fWheelFriction / pHandling->fMass;
#ifdef FIX_BUGS
brake *= CTimer::GetTimeStepFix();
#endif
} }
} }


@@ -888,10 +881,10 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
*wheelState = WHEEL_STATE_SKIDDING; *wheelState = WHEEL_STATE_SKIDDING;
} }


float l = Sqrt(speedSq); float speed = Sqrt(speedSq);
float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss; float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss;
right *= adhesion * tractionLoss / l; right *= adhesion * tractionLoss / speed;
fwd *= adhesion * tractionLoss / l; fwd *= adhesion * tractionLoss / speed;
} }


if(fwd != 0.0f || right != 0.0f){ if(fwd != 0.0f || right != 0.0f){
@@ -923,9 +916,19 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
else else
turnDirection = direction; turnDirection = direction;



// Curious: there is a perfectly good ApplyMoveSpeed() function that
// takes "velocity impulses", but instead we use the ApplyMoveForce()
// function which takes a "force impulse" instead and then fix up the
// difference by multiplying in the vehicle mass (F=ma). Possibly
// evidence that an optimising compiler re-arranged the arithmetic?
float impulse = speed*m_fMass; float impulse = speed*m_fMass;
float turnImpulse = turnSpeed*GetMass(wheelContactPoint, turnDirection); float turnImpulse = turnSpeed*GetMass(wheelContactPoint, turnDirection);


#ifdef FIX_BUGS
impulse = impulse * CTimer::GetTimeStepFix();
turnImpulse = turnImpulse * CTimer::GetTimeStepFix();
#endif
ApplyMoveForce(impulse * direction); ApplyMoveForce(impulse * direction);
ApplyTurnForce(turnImpulse * turnDirection, wheelContactPoint); ApplyTurnForce(turnImpulse * turnDirection, wheelContactPoint);
} }
@@ -949,7 +952,14 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
bAlreadySkidding = false; bAlreadySkidding = false;
#endif #endif


// how much force we want to apply in these axes // Velocity impulses fwd and right. Units are like meters per second.
// This function was written assuming a fixed FPS, so a correction is made
// right at the end before actually applying these impulses.
//
// Note that many functions in this engine deal with "force impulses" rather
// than "velocity impulses". They are directly related: F=ma. It is
// possible that the original devs actually used force impulses here but
// an optimising compiler re-arranged their maths.
float fwd = 0.0f; float fwd = 0.0f;
float right = 0.0f; float right = 0.0f;


@@ -966,7 +976,12 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
bAlreadySkidding = true; bAlreadySkidding = true;
*wheelState = WHEEL_STATE_NORMAL; *wheelState = WHEEL_STATE_NORMAL;


#ifdef FIX_BUGS
// Everything else here is timestep independent, let's stay with this theme to keep the rest of the FPS bugfixes simpler.
adhesion *= CTimer::GetDefaultTimeStep();
#else
adhesion *= CTimer::GetTimeStep(); adhesion *= CTimer::GetTimeStep();
#endif
if(bAlreadySkidding) if(bAlreadySkidding)
adhesion *= pHandling->fTractionLoss; adhesion *= pHandling->fTractionLoss;


@@ -979,20 +994,10 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
if(contactSpeedRight != 0.0f){ if(contactSpeedRight != 0.0f){
// exert opposing force // exert opposing force
right = -contactSpeedRight/wheelsOnGround; right = -contactSpeedRight/wheelsOnGround;
#ifdef FIX_BUGS
// contactSpeedRight is independent of framerate but right has timestep as a factor
// so we probably have to fix this
right *= CTimer::GetTimeStepFix();
#endif


if(wheelStatus == WHEEL_STATUS_BURST){ if(wheelStatus == WHEEL_STATUS_BURST){
float fwdspeed = Min(contactSpeedFwd, fBurstBikeSpeedMax); float fwdspeed = Min(contactSpeedFwd, fBurstBikeSpeedMax);
#ifdef FIX_BUGS
// Keep the effect running at the same frequency even when the game is at high FPS
right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod) * CTimer::GetLogicalFramesPassed();
#else
right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod); right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod);
#endif
} }
} }


@@ -1009,12 +1014,6 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
} }
}else if(contactSpeedFwd != 0.0f){ }else if(contactSpeedFwd != 0.0f){
fwd = -contactSpeedFwd/wheelsOnGround; fwd = -contactSpeedFwd/wheelsOnGround;
#ifdef FIX_BUGS
// contactSpeedFwd is independent of framerate but fwd has timestep as a factor
// so we probably have to fix this
fwd *= CTimer::GetTimeStepFix();
#endif

if(!bBraking){ if(!bBraking){
if(m_fGasPedal < 0.01f){ if(m_fGasPedal < 0.01f){
if(IsBike()) if(IsBike())
@@ -1025,9 +1024,6 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass; brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass;
else else
brake = mod_HandlingManager.fWheelFriction / m_fMass; brake = mod_HandlingManager.fWheelFriction / m_fMass;
#ifdef FIX_BUGS
brake *= CTimer::GetTimeStepFix();
#endif
} }
} }


@@ -1078,6 +1074,10 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee


float impulse = speed*m_fMass; float impulse = speed*m_fMass;
float turnImpulse = speed*GetMass(wheelContactPoint, direction); float turnImpulse = speed*GetMass(wheelContactPoint, direction);
#ifdef FIX_BUGS
impulse = impulse * CTimer::GetTimeStepFix();
turnImpulse = turnImpulse * CTimer::GetTimeStepFix();
#endif
CVector vTurnImpulse = turnImpulse * direction; CVector vTurnImpulse = turnImpulse * direction;
ApplyMoveForce(impulse * direction); ApplyMoveForce(impulse * direction);


0 comments on commit ba832f9

Please sign in to comment.