Fix all(?) vehicle handling problems at high FPS. Refactor FIX_BUGS in ProcessWheel() to be much simpler.

This commit is contained in:
Veyrdite 2021-07-23 22:45:34 +10:00
parent 879280bcf7
commit ba832f93e8
4 changed files with 85 additions and 51 deletions

View File

@ -590,7 +590,12 @@ CPhysical::ApplyAirResistance(void)
}else if(GetStatus() != STATUS_GHOST){
float f = Pow(1.0f/Abs(1.0f + m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr()), CTimer::GetTimeStep());
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;
#endif
}
}
@ -1071,6 +1076,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
#ifdef FIX_BUGS
@ -1107,6 +1116,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
impulseB = (speedSum - fOtherSpeedB) * massB;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit;
@ -1140,6 +1153,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * massA;
impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit;
@ -1174,6 +1191,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * massA;
impulseB = (speedSum - fOtherSpeedB) * massB;
#ifdef FIX_BUGS
impulseA *= CTimer::GetTimeStepFix();
impulseB *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit;
@ -1214,6 +1235,9 @@ CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint)
// not really impulse but speed
// maybe use ApplyFrictionMoveForce instead?
fImpulse = -fOtherSpeed;
#ifdef FIX_BUGS
fImpulse *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass;
if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
CVector vImpulse = frictionDir*fImpulse;
@ -1235,6 +1259,9 @@ CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint)
frictionDir = vOtherSpeed * (1.0f/fOtherSpeed);
#endif
fImpulse = -fOtherSpeed * m_fMass;
#ifdef FIX_BUGS
fImpulse *= CTimer::GetTimeStepFix();
#endif
impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5;
if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
ApplyFrictionMoveForce(frictionDir*fImpulse);

View File

@ -857,8 +857,12 @@ CAutomobile::ProcessControl(void)
(m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f))
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();
#endif
bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING);
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...
@ -1058,12 +1062,7 @@ CAutomobile::ProcessControl(void)
float rearBrake = brake;
float rearTraction = traction;
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;
#endif
if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){
m_fTireTemperature += 0.005*CTimer::GetTimeStep();
if(m_fTireTemperature > 2.0f)
@ -1072,8 +1071,11 @@ CAutomobile::ProcessControl(void)
}else if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)){
rearBrake = 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());
#endif
}else if(m_fTireTemperature > 1.0f){
rearTraction *= m_fTireTemperature;
}

View File

@ -125,7 +125,12 @@ cTransmission::CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, fl
float targetVelocity = Gears[gear].fMaxVelocity*speedMul*fCheat;
float accel = (targetVelocity - fVelocity) * (fEngineAcceleration*accelMul) / Abs(targetVelocity);
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();
#endif
else
fAcceleration = 0.0f;
return fAcceleration;

View File

@ -788,7 +788,14 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
bAlreadySkidding = false;
#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 right = 0.0f;
@ -804,7 +811,12 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
bAlreadySkidding = true;
*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();
#endif
if(bAlreadySkidding)
adhesion *= pHandling->fTractionLoss;
@ -812,28 +824,17 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
if(contactSpeedRight != 0.0f){
// exert opposing force
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){
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);
#endif
}
}
if(bDriving){
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 > adhesion)
right = adhesion;
@ -843,11 +844,6 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
}
}else if(contactSpeedFwd != 0.0f){
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(m_fGasPedal < 0.01f){
@ -859,9 +855,6 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass;
else
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;
}
float l = Sqrt(speedSq);
float speed = Sqrt(speedSq);
float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss;
right *= adhesion * tractionLoss / l;
fwd *= adhesion * tractionLoss / l;
right *= adhesion * tractionLoss / speed;
fwd *= adhesion * tractionLoss / speed;
}
if(fwd != 0.0f || right != 0.0f){
@ -923,9 +916,19 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon
else
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 turnImpulse = turnSpeed*GetMass(wheelContactPoint, turnDirection);
#ifdef FIX_BUGS
impulse = impulse * CTimer::GetTimeStepFix();
turnImpulse = turnImpulse * CTimer::GetTimeStepFix();
#endif
ApplyMoveForce(impulse * direction);
ApplyTurnForce(turnImpulse * turnDirection, wheelContactPoint);
}
@ -949,7 +952,14 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
bAlreadySkidding = false;
#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 right = 0.0f;
@ -966,7 +976,12 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
bAlreadySkidding = true;
*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();
#endif
if(bAlreadySkidding)
adhesion *= pHandling->fTractionLoss;
@ -979,20 +994,10 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
if(contactSpeedRight != 0.0f){
// exert opposing force
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){
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);
#endif
}
}
@ -1009,12 +1014,6 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
}
}else if(contactSpeedFwd != 0.0f){
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(m_fGasPedal < 0.01f){
if(IsBike())
@ -1025,9 +1024,6 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee
brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass;
else
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 turnImpulse = speed*GetMass(wheelContactPoint, direction);
#ifdef FIX_BUGS
impulse = impulse * CTimer::GetTimeStepFix();
turnImpulse = turnImpulse * CTimer::GetTimeStepFix();
#endif
CVector vTurnImpulse = turnImpulse * direction;
ApplyMoveForce(impulse * direction);