Open KarlZeilhofer opened 5 years ago
I'm glad to hear you liked the game :)
The behavior of the car is mostly defined by the tire models (tire.hpp/tire.cpp) initialized in the constructor of the car class (car.cpp). I haven't been working on this project for a while, but I think it could be nice to load these car parameters from a JSON or something. I might actually implement that. At some point I didn't know anymore what is good and what is bad (you become blind to your own game) so it would be nice to have someone to give his opinion of the balance of the car etc.
Thanks for that quick answer. I'll give it a try to change parameters in the sources. Very easy to use in Qt is an ini-file (QSettings). Would you accept a pull request, if it implements the car tuning in an QSettings ini-file?
I'll accept if you use XML or JSON :) QSettings is kinda deprecated and not the best option for this kind of stuff in my opinion. The game already uses QtXml for tracks. For JSON this is like magic: https://github.com/nlohmann/json and I prefer it to Qt's JSON implementation. That could be just added to src/game/contrib/nlohmann/ or something like that. IMO the most easy-to-use solution as the API is just like std::map.
For the default parameters we could have something like:
{
"power": 5000,
"mass": 1500,
"frontFriction": 0.85,
"rearFriction": 0.95,
...
}
Maybe even tire locations.
I also noticed that some basic parameters are in car.hpp in Car::Description. This would indeed need some refactoring :)
Indeed, the json lib from nlohmann looks very impressive. But currently I'm stuck on compiling the game at all, see #39
I'm just collecting my suggestions and thoughts here:
physicsComponent().setMomentOfInertia(desc.mass * 3.0);
for a rectangle, the inertia is 1/12(a^2+b^2) x mass. For a car I suggest 1.4x4m, which gives about 1.5 x mass instead of programmed 3 x mass. This is the inertia for the rotation about the center of gravity. Translated to the rear axle, this gives about 3.75m, if the axle is 1.5m behind the center of gravity.
Suggested value is 1.5, as it feels a lot better during game play.
@juzzlin Do you know, what center of rotation is assumed for that inertia?
m_angularVelocity += totAngularAcceleration * step + m_angularImpulse;
in myphysicscomponent.cpp
.
Here the angular impulse is used, as it would be an angular velocity. If it is a physical impulse, it should be divided by the momentum of inertia first.
Why is the location of front tires different than that of the rear tires?
MCVector3dF Car::leftFrontTireLocation() const
{
return m_leftFrontTire->location();
}
...
MCVector3dF Car::leftRearTireLocation() const
{
return MCMathUtil::rotatedVector(m_leftRearTirePos, angle()) + MCVector2dF(location());
}
Both are symmetrically placed around the car's center (of gravity?).
, m_leftFrontTirePos(14, 9, 0)
, m_rightFrontTirePos(14, -9, 0)
, m_leftRearTirePos(-14, 9, 0)
, m_rightRearTirePos(-14, -9, 0)
Even though, the units are a bit strange. This are definitely not meters.
@juzzlin can you say something about the unit used for the tire locations?
EDIT: the whole car has a length of 48 units, and the axle distance is 28, 58% of the length. To compare it with a BMW 1er series, they have 266cm/422cm = 63%. A VW Golf has 258/420 = 61%. The tire distances could be increased a bit therefore.
As the tire position from above, also the calculations for the speed in km/h is strange:
m_speedInKmh = m_absSpeed * 3.6 * 2.75;
There is some factor of 2.75 (EDIT: is a fuzzy factor to look good)
but this applied to a wheel location of 14, would give 5m distance from the center of gravity in driving direction. That's a factor of ~4 off (1.25m would be plausible)
In scene.cpp: static const float METERS_PER_UNIT = 0.05f;
So each Unit in the scene represents 5cm physical length. But that factor doesn't seem to be taken into account for the physics.
It seems when steering at higher speeds e.g. to the left side, the tail of the car moves to the right in first place. I have to investigate that further. The test I did: drive fast along a straight line, steer, and observe the distance between the tail and that line. I think this is what gives the feeling of "head dominated" dynamics, like driving a tadpole (German: Kaulquappe) or a car with rear wheel steering.
The braking distance ist about 4 car lengths with 100km/h speed. with a car of 4m length this is about 16m. That is way too short. About 36m is the minimum for top sports cars.
is set in SI units to 9.81m/s² :+1:
Here comes the accelerationFriction
in, which is by default 0.75,
set to desc.accelerationFriction = 0.55f * Game::instance().difficultyProfile().accelerationFrictionMultiplier(true);
for humans and set to
desc.accelerationFriction = (0.3f + 0.4f * float(index + 1) / NUM_CARS) * Game::instance().difficultyProfile().accelerationFrictionMultiplier(false);
for bots.
The friction results to 0.55 x (0.70, 0.85, 1.0) = (0.39, 0.47, 0.55) for humans for easy, medium and hard profile, refer to float DifficultyProfile::accelerationFrictionMultiplier(bool isHuman) const
.
The max Force for acceleration is now calculated by the mass, the gravity and this friction factor - seems to be plausible. Since we have a 2 wheel drive, and best tires have about 1.1 friction and 1.1/2 = 0.55. Why lower skilled players get lower friction, that's somehow strange, but ok.
The engines driving force is calculated with the scaled velocity (in scene units/second), which gives a wrong force by a factor of about ten times too low.
The center of rotation is assumed to be the center of the object. Of course that's not entirely correct as many other things.
According to the documentation in PhysicsComponent header the angular "impulse" is supposed to be rad/s. I did this many years ago so I don't remember anymore why is that. Seems to be incorrect.
The tire locations are partly like that because they should also match the graphics. The units are "scene units" and I remember there's a conversion factor somewhere. Yeah...seems to be in MCWorld. There's metersPerUnit() which is then used in some calculations. I had plans to get rid of this but didn't have enough time.
The speed calculation has a magic number to make the value look "better" in the game. The raw speed should be in m/s. I hope it's not some messed up "scene units" / s :)
I like your observations and appreciate all improvements and corrections to the physics calculations.
One more thing in the case you haven't noticed. You can run some physics framework tests in the build dir by:
$ ctest
It starts the CTest test runner and runs some unit tests that are done with Qt's test framework.
@juzzlin Thanks for your comments and help.
I used git grep metersPerUnit
to find out the value is being used in MCImpulseGenerator
. However, it would be cool if everything was in meters.
You are right about the braking distance. It's way too powerful and also goes to reverse too easily. I think that it was needed so that the computer players could brake hard enough. Of course that could be handled also in some other way like making it realistic for the human players only.
@juzzlin due to this lines
m_absSpeed = physicsComponent().speed();
m_speedInKmh = m_absSpeed * 3.6 * 2.75;
I don't believe the car's speed is in m/s.
The length of a car is 48 scene units, which is 2.4m - that's quite short, isn't it? I think the constant METERS_PER_UNIT
should be changed from 0.05 to 0.1, then we have a 4.8m long car.
Of course that could be handled also in some other way like making it realistic for the human players only.
:smile: I think, physics should be the same for all players?! Otherwise it's unfair.
The linear velocity (calculation) is in m/s, because it depends only on mass, acceleration etc. But the projection to the game scene and its dimensions is another thing :) So, the speed is in m/s, but it's incorrect with respect to the size of the car.
@juzzlin one question: Any suggestions, where I should do the calculations between scene coordinates and the physical coordinates?
I have pushed now all my in-code comments, if you would like to have a look: https://github.com/KarlZeilhofer/DustRacing2D/tree/devs/physics
I'm using the Qt Creators TODO plugin, to locate that comments easily. But you could also grep for TODO
and FIXME
.
As a first step, I would like to do following things:
1/12 * (a² + b²)
MCVector3dF Car::leftFrontTireLocation()
and corresponding functions. impulses
to force or momentum. Looks like a good list.
Maybe it'd be the "best" to call the conversion functions of MCWorld on-the-fly wherever needed..? Of course the perfect solution would be to have everything in SI (except rendering stuff in renderers, view objects and scene) but I guess that would be too big of a change for now :)
You are right with the velocity. But the transformation is needed somewhere.
In first place, I'll try to improve the physics as they are. One big thing I understood now, is that the tires only generate forces, when they already slide. That algorithm seems to be too simple/unstable - somehow. But describing the system isn't an easy task, I think - though I once learned that thing at the university. If you have Qt installed, you may try to run https://github.com/KarlZeilhofer/inverse-tripple-pendulum
It's also a sort of a game :smile:
I tried your app and it worked very well. I just wish I understood the math :)
The tire model uses a "trick" I learned somewhere to limit and lose grip and make it slide:
impulse.clampFast(parent().physicsComponent().mass() * 7.0f * m_car.tireWearFactor());
I wrote the pendulum app some years ago, I'm not sure, if I could do the maths today again. :grin:
I think, this code generates the sliding anyway:
MCVector2dF normalForceVector =
MCVector2dF::projection(tireVelocityMaxUnityVector, tireAxisVector) *
(m_isOffTrack ? m_offTrackFriction : m_friction) *
-MCWorld::instance().gravity().k() * parent().physicsComponent().mass();
As I understand it, it generates a force proportional to the sliding speed, which is clamped here already, since tireVelocityMaxUnityVector
is limited to length 1. Therefore I don't see the necessity for the 2nd limitation you mentioned. Your 2nd limitation could also be written as
mass . 0.7 . g
. So you assume a maximum grip coefficient of 70% with g=10m/s². The 1st formula clips basically to m_friction
.
@KarlZeilhofer Are you still working on this?
Not really. I couldn't manage to convert everything to metric units, and a realistic physics engine is also not easy to implement. Though I've tweaked some values, and in my opinion it is now more fun to play.
I've published my current WIP in commit adfd59a3c093a4e7e7e305221a0c8d3af3a18d6b
Would it be possible to make a cleaned-up pull request so it would be easier to see the changes?
Hi, great Game, just played it half an hour! :+1: Thank you very much for providing such a great content.
Is there anything we can do about the physics of the car? It feels like driving a front motor car with rear wheels drive and 70/30 percent weight distribution between front and rear.
Would love to have a 4 wheel drive with balanced weight distribution :)
Greets, Karl
(v2.0.1 from the ubuntu repo on linux mint)