From ec49fe2b27e724b4129990cae52ff8cf6c34e799 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 1 Aug 2018 23:09:26 +0200 Subject: [PATCH 01/10] rwgame: Add more interesting vehicles to DebugStates Signed-off-by: Paul Kocialkowski --- rwgame/states/DebugState.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rwgame/states/DebugState.cpp b/rwgame/states/DebugState.cpp index f293b7cb..0a6efd24 100644 --- a/rwgame/states/DebugState.cpp +++ b/rwgame/states/DebugState.cpp @@ -153,7 +153,9 @@ std::shared_ptr DebugState::createVehicleMenu() { {"Police", 116}, {"Ambulance", 106}, {"Bobcat", 112}, {"Banshee", 119}, {"Rhino", 122}, {"Barracks", 123}, {"Rumpo", 130}, {"Columbian", 138}, {"Dodo", 126}, - {"Speeder", 142}, {"Yakuza", 136}, + {"Speeder", 142}, {"Yakuza", 136}, {"Cheetah", 105}, + {"Ambulance", 106}, {"FBI", 107}, {"Mafia", 134}, + {"Infernus", 101}, }; for (auto& e : kVehicleTypes) { From 094513681b07a701391d73a0c2b1f3f74a9a9fb4 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 1 Aug 2018 23:36:53 +0200 Subject: [PATCH 02/10] rwengine: Cap maxmimum velocity to the adapted maximum from handling A specific coefficient is introduced to adapt the value to the physics engine, since converting the value from km/h gives an unrealisticly high maximum speed. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index d43facc5..98f2f2d7 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -307,9 +307,20 @@ void VehicleObject::tickPhysics(float dt) { if (physVehicle) { // todo: a real engine function float velFac = info->handling.maxVelocity; + float velocity = collision->getBulletBody()->getLinearVelocity().length(); + float velocityMax = velFac / 9.f; float engineForce = info->handling.acceleration * throttle * velFac; float brakeF = getBraking(); + if (velocity > velocityMax) { + btVector3 v = collision->getBulletBody()->getLinearVelocity().normalized(); + + velocity = velocityMax; + v *= velocity; + + collision->getBulletBody()->setLinearVelocity(v); + } + if (handbrake) { brakeF = 5.f; } From 6c66f6151901282f0f85b1b2cabbc3909250c1c2 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 1 Aug 2018 23:42:23 +0200 Subject: [PATCH 03/10] rwengine: Implement actual braking when the vehicle is going forward This implements proper braking by detecting a brake condition with a negative throttle and a positive forward velocity. Under this condition, the engine is stopped and brake is applied. A mass coefficient is introduced to quantify how heavy a vehicle is, so that more braking can be applied to heavy vehicles, that have higher inertia. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index 98f2f2d7..00ba5095 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -308,9 +308,13 @@ void VehicleObject::tickPhysics(float dt) { // todo: a real engine function float velFac = info->handling.maxVelocity; float velocity = collision->getBulletBody()->getLinearVelocity().length(); + float velocityForward = physVehicle->getCurrentSpeedKmHour() / 3.6f; float velocityMax = velFac / 9.f; float engineForce = info->handling.acceleration * throttle * velFac; float brakeF = getBraking(); + // Mass coefficient, that quantifies how heavy a vehicle is and excludes + // light vehicles. + float kM = (std::max(1500.f, info->handling.mass) - 1500.f) / 1500.f; if (velocity > velocityMax) { btVector3 v = collision->getBulletBody()->getLinearVelocity().normalized(); @@ -323,6 +327,9 @@ void VehicleObject::tickPhysics(float dt) { if (handbrake) { brakeF = 5.f; + } else if (throttle < 0.f && velocityForward > velocityMax / 8.f) { + engineForce = 0.f; + brakeF = 2.f * std::min(1.f + kM, 4.f); } for (int w = 0; w < physVehicle->getNumWheels(); ++w) { @@ -336,7 +343,7 @@ void VehicleObject::tickPhysics(float dt) { } float brakeReal = - 5.f * info->handling.brakeDeceleration * + 8.f * info->handling.brakeDeceleration * (wi.m_bIsFrontWheel ? info->handling.brakeBias : 1.f - info->handling.brakeBias); physVehicle->setBrake(brakeReal * brakeF, w); From 3ae98dd93ace401a80553e1623886386b2117e91 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 1 Aug 2018 23:48:27 +0200 Subject: [PATCH 04/10] rwengine: Increase the engine force based on the mass Since heavy vehicles have more inertia, they require more engine force to be moved sufficiently fast. Without this extra force, many heavy vehicles are unbearably slow to drive. Reuse the mass coefficient that was introduced to increase the engine force accordingly. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index 00ba5095..e653edfa 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -316,6 +316,10 @@ void VehicleObject::tickPhysics(float dt) { // light vehicles. float kM = (std::max(1500.f, info->handling.mass) - 1500.f) / 1500.f; + // Increase the engine force based on the mass by up to 4 times. + // Heavy vehicles need extra engine force to be reactive enough. + engineForce *= std::min(1.f + kM, 4.f); + if (velocity > velocityMax) { btVector3 v = collision->getBulletBody()->getLinearVelocity().normalized(); From dce3b9af3aa12fb319c6869302d507968ed446e1 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 1 Aug 2018 23:52:13 +0200 Subject: [PATCH 05/10] rwengine: Increase the engine force when starting up Most vehicles are still slow to start and manoeuvre. For instance, it is rather hard to precisely park in a parking space. In order to make vehicles more reactive when starting (either forward or backward), the engine force is increased at first and progressively decreased to its nominal force, following the velocity increase up to half the maximum velocity. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index e653edfa..50f70769 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -320,6 +320,15 @@ void VehicleObject::tickPhysics(float dt) { // Heavy vehicles need extra engine force to be reactive enough. engineForce *= std::min(1.f + kM, 4.f); + // Give vehicles a boost when starting (forward or backward) to reduce + // general sluggishness. The engine force is multiplied by 1.5 before + // reaching velocityMax / 4 and then reduces down to nominal force as + // velocity reaches velocityMax / 2. + if (velocity < velocityMax / 4.f) + engineForce *= 1.5f; + else if (velocity < velocityMax / 2.f) + engineForce *= 1.f + 0.5f * (2.f - velocity / (velocityMax / 4.f)); + if (velocity > velocityMax) { btVector3 v = collision->getBulletBody()->getLinearVelocity().normalized(); From 77b3df037ede40687b6d69866ca59c907155c956 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 2 Aug 2018 19:04:12 +0200 Subject: [PATCH 06/10] rwengine: Adapt the nominal engine force for our physics This introduces a coefficient to adapt the nominal engine force to our physics and ensure that top speed is not reached too fast. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index 50f70769..a5369e73 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -310,7 +310,10 @@ void VehicleObject::tickPhysics(float dt) { float velocity = collision->getBulletBody()->getLinearVelocity().length(); float velocityForward = physVehicle->getCurrentSpeedKmHour() / 3.6f; float velocityMax = velFac / 9.f; - float engineForce = info->handling.acceleration * throttle * velFac; + // The engine force is calculated from the acceleration and max velocity + // of the vehicle, with a specific coefficient to make it adapted to + // Bullet physics and avoid reaching top speed too fast. + float engineForce = info->handling.acceleration * throttle * velFac / 1.2f; float brakeF = getBraking(); // Mass coefficient, that quantifies how heavy a vehicle is and excludes // light vehicles. From 20ac5b2a3938646f58806eb947762b891893bed9 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 2 Aug 2018 21:23:54 +0200 Subject: [PATCH 07/10] rwengine: Slow down engine force when the vehicle is braking This introduces a linear reduction of the engine force when braking is applied. The division factor increases with the steering angle to max steering angle ratio, reducing the engine force by a factor of up to 1.25. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index a5369e73..9cabdb62 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -310,6 +310,8 @@ void VehicleObject::tickPhysics(float dt) { float velocity = collision->getBulletBody()->getLinearVelocity().length(); float velocityForward = physVehicle->getCurrentSpeedKmHour() / 3.6f; float velocityMax = velFac / 9.f; + float steerValue = 0.f; + float steerLimit = glm::radians(info->handling.steeringLock); // The engine force is calculated from the acceleration and max velocity // of the vehicle, with a specific coefficient to make it adapted to // Bullet physics and avoid reaching top speed too fast. @@ -318,6 +320,19 @@ void VehicleObject::tickPhysics(float dt) { // Mass coefficient, that quantifies how heavy a vehicle is and excludes // light vehicles. float kM = (std::max(1500.f, info->handling.mass) - 1500.f) / 1500.f; + unsigned int count = 0; + + // Get the global vehicle steering value (for front wheels only). + for (int w = 0; w < physVehicle->getNumWheels(); ++w) { + btWheelInfo& wi = physVehicle->getWheelInfo(w); + + if (wi.m_bIsFrontWheel) { + steerValue += physVehicle->getSteeringValue(w); + count++; + } + } + + steerValue /= count; // Increase the engine force based on the mass by up to 4 times. // Heavy vehicles need extra engine force to be reactive enough. @@ -332,6 +347,12 @@ void VehicleObject::tickPhysics(float dt) { else if (velocity < velocityMax / 2.f) engineForce *= 1.f + 0.5f * (2.f - velocity / (velocityMax / 4.f)); + // Reduce the engine force when steering, by a factor of up to 1.25 for + // the maximum steering angle, linearly back to nominal value when no + // steering is applied. + if (std::abs(steerValue) > steerLimit / 8.f && velocity > velocityMax / 3.f) + engineForce /= 1.f + std::abs(steerValue) / (4.f * steerLimit); + if (velocity > velocityMax) { btVector3 v = collision->getBulletBody()->getLinearVelocity().normalized(); From e3e8b3acb9db3d2bbf3242c491a9173a40822cd3 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 2 Aug 2018 21:29:42 +0200 Subject: [PATCH 08/10] rwengine: Reduce vehicle roll influence to avoid easily flipping wheels The current roll influence value makes it much too easy to flip vehicles upside down. More generally, the vehicles tend to not stick to the ground enough as they are too easily moved around when hit with another vehicle. Reduce the roll influence value for vehicles to make them stick more to the ground. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index 9cabdb62..89dc6e24 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -139,7 +139,10 @@ VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos, kC * 2.f * btSqrt(wi.m_suspensionStiffness); wi.m_wheelsDampingRelaxation = kR * 2.f * btSqrt(wi.m_suspensionStiffness); - wi.m_rollInfluence = 0.30f; + // Roll influence prevents cars from easily flipping wheels. + // Tune with care! + wi.m_rollInfluence = 0.02f; + float halfFriction = tuning.m_frictionSlip * 0.5f; wi.m_frictionSlip = halfFriction + From 17b6c13a6ab7c26f87fce091f43bd51c97370b39 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 2 Aug 2018 21:36:36 +0200 Subject: [PATCH 09/10] rwgame: Adapt the friction slip based on handling data to ease steering A constant friction slip for all vehicles does not seem to work very well and most are rather hard to steer with. This introduces a somewhat empirical formula to get a rather sane friction slip value based on handling data, where coefficients are used in a way compatible with the required friction slip value for most vehicles. This is by far not a perfect approach and the steering on some vehicles still feels out of line with the realistic expected behaviour, but most remain fairly drivable. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index 89dc6e24..fcdcee83 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -104,7 +104,20 @@ VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos, float travel = fabs(info->handling.suspensionUpperLimit - info->handling.suspensionLowerLimit); - tuning.m_frictionSlip = 3.f; + float maxVelocity = info->handling.maxVelocity; + float accelerationFloor = std::max(info->handling.acceleration, 30.f); + float massFloor = std::min(info->handling.mass, 3000.f); + // The friction slip represents the friction coefficient between each wheel + // and the ground. The higher the coefficient, the more slippery the contact + // gets between the wheel and the ground. Fast vehicles (with a significant + // acceleration and max speed for a low mass) tend to need a high + // coefficient (> 5.f) to allow properly turning when steering. On the other + // hand, slow vehicles tend to need a lower coefficient or they will be too + // reactive when turning. This (purely empirical) formula is an attempt to + // apply this idea, with floors to avoid too much divergence. + // For some reason, the calculation with handling info gives us a value in + // the right range, with an offset to set a sane base. + tuning.m_frictionSlip = 1.8f + maxVelocity * accelerationFloor / massFloor; tuning.m_maxSuspensionTravelCm = travel * 100.f; physVehicle = From ff9ccd5f07a81332c11d3dce9e45433d40a735c0 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 2 Aug 2018 23:51:13 +0200 Subject: [PATCH 10/10] rwengine: Set the linear velocity to a zero vector when stopped Vehicles have a tendency to move on their own when they are stopped, due to their interactions with other surfaces that result in a non-zero velocity. Since we have a threshold with the isStopped() method, fix this situation by setting the linear velocity and wheels rotation (that otherwise also rotate on their own) to zero vectors. Signed-off-by: Paul Kocialkowski --- rwengine/src/objects/VehicleObject.cpp | 18 ++++++++++++++++++ rwengine/src/objects/VehicleObject.hpp | 2 ++ 2 files changed, 20 insertions(+) diff --git a/rwengine/src/objects/VehicleObject.cpp b/rwengine/src/objects/VehicleObject.cpp index fcdcee83..843952bc 100644 --- a/rwengine/src/objects/VehicleObject.cpp +++ b/rwengine/src/objects/VehicleObject.cpp @@ -161,6 +161,8 @@ VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos, halfFriction + halfFriction * (front ? info->handling.tractionBias : 1.f - info->handling.tractionBias); + + wheelsRotation.push_back(0.f); } setModel(getVehicle()->getModel()); @@ -385,8 +387,24 @@ void VehicleObject::tickPhysics(float dt) { brakeF = 2.f * std::min(1.f + kM, 4.f); } + if (isStopped() && std::abs(throttle) < 0.1f) { + btVector3 v = collision->getBulletBody()->getLinearVelocity(); + v.setX(0.f); + v.setY(0.f); + + collision->getBulletBody()->setLinearVelocity(v); + + for (int w = 0; w < physVehicle->getNumWheels(); ++w) { + btWheelInfo& wi = physVehicle->getWheelInfo(w); + wi.m_rotation = wheelsRotation[w]; + } + } + for (int w = 0; w < physVehicle->getNumWheels(); ++w) { btWheelInfo& wi = physVehicle->getWheelInfo(w); + + wheelsRotation[w] = wi.m_rotation; + if (info->handling.driveType == VehicleHandlingInfo::All || (info->handling.driveType == VehicleHandlingInfo::Forward && wi.m_bIsFrontWheel) || diff --git a/rwengine/src/objects/VehicleObject.hpp b/rwengine/src/objects/VehicleObject.hpp index 12753435..a884c8e3 100644 --- a/rwengine/src/objects/VehicleObject.hpp +++ b/rwengine/src/objects/VehicleObject.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ private: float throttle{0.f}; float brake{0.f}; bool handbrake = true; + std::vector wheelsRotation; Atomic* chassishigh_ = nullptr; Atomic* chassislow_ = nullptr;