GDSim v0.4a - Autocross and Custom Setups

  • Thread starter Wolfe
  • 62 comments
  • 34,624 views
No worries. 🐺 I will add an edit to help clarify this part.

What we're doing here is actually treating a wheel's angular velocity more like an independent physics property, acted upon by the sum of torques on each wheel as shown in section IV. The wheels have their own virtual mass and momentum together with that angular velocity, or spin.

The drive force at the wheels is a product of the tire formula (whether it's the Pacejka formula, Brush formula, or another formula), which involves a comparison between spin * radius and the Z velocity (in a localized reference frame) to calculate the slip ratio, (spin * radius - z_vel) / abs(z_vel). So spin has a naturally indirect effect on the force value moving the car with the driven wheels. From Newton's third law, the same tire force that moves the car also exerts a torque back on the wheel, which is used to initialize the torque sum each frame.

In other words, there are distinct relationships of powertrain <-> wheel <-> tire <-> surface.
Thanks for taking the time to reply. So having re-read section IV, I think my mistake was thinking that I needed to somehow calculate/define spin e.g. spin = fwd_velo / radius, whereas it was the actual code/methods outlined in section IV that calculated the spin value.

I suspect I need to re-read the guide and rebuild my implementation as I don't think I've got the all the elements in the right order.
 
I've added a note in section III about spin being calculated in the next part, which should help make it a little clearer.
Thanks, it did. But having done some debugging it appears there is definitely something wrong in my implementation of the code from section IV. As even when just rolling my car down a slope with no power and just using the freewheel function to update spin etc.
the spin value just keeps rising and doesn't drop even when the car comes to a halt. Time to re-read section IV!
 
hi, dumb comment but, when i tried out the slip angle equation, simply just taking the local velocity and stuff, it appeared the the local velocity from this equation:

var local_vel = global_transform.basis.xform_inv((global_transform.origin - prev_pos) / delta)
z_vel = -local_vel.y
var planar_vect = Vector2(local_vel.x, local_vel.y).normalized()
prev_pos = global_transform.origin

makes the local velocity vector have all it's parameters return zero no matter what,
what should i do?
i am new to godot.

Edit: nvm I figured it out!
 
Last edited:
Thanks for the great tutorial! i've also been playing around with godot for some time now. Always wanted to make a rally game myself. The behaviour of the car i got with your tutorial is pretty amazing! A question though: Are you multiplying your gear ratio with final drive / diff ratio in your gear_ratio() method? I have it set so and i had to divide drive_inertia with final_drive to get it working properly on my setup. Anyways keep up with the good work!
 
Thanks for the great tutorial! i've also been playing around with godot for some time now. Always wanted to make a rally game myself. The behaviour of the car i got with your tutorial is pretty amazing! A question though: Are you multiplying your gear ratio with final drive / diff ratio in your gear_ratio() method? I have it set so and i had to divide drive_inertia with final_drive to get it working properly on my setup. Anyways keep up with the good work!
Thanks! :) Yes, the gear_ratio() method includes the final drive ratio. I'm not sure what you bumped into there.
 
Hey there.

I'd like to ask you, do you by any chance have your car facing the negative X axis by default?

I'm asking this because I set up mine facing the negative Y (as is usually the convention people use), but then to get my car moving (badly) forward I had to switch this code to use the basis's Y axis instead of X:
Code:
if $RayCast.is_colliding():
    car.add_force(global_transform.basis.y * x_force, contact)
    car.add_force(normal * y_force, contact)
    car.add_force(global_transform.basis.y * z_force, contact)

If I use the original code my car slides sideways, toward negative X.


EDIT: actually I just noticed I overlooked what you said right before posting that bit of code:
(Note: this example will use the X basis; I'll show how I rotated the surface normal when I bring things around to the brush formula, because I'm drawing from different stages of progress and don't want to mix up +/-)

I'm rather confused, though.

To be clear about where I'm standing: I implemented all the code up to part 4, except the steering part.
The car is slightly slippery when idling, which I presume is expected for now, and it moves if I accelerate forward. Doesn't seem to brake while moving, but it does move forward or backward from idle depending which way I accelerate. Takes ages to lose speed, but I guess that's also expected for now.
 
Last edited:
I'd like to ask you, do you by any chance have your car facing the negative X axis by default?
I follow the Godot convention of facing negative Z, but the SpringArm node must be rotated to point down, so its forward basis is actually negative Y:

axes.jpg


If the car is "fidgeting" on the tires at idle, the physics FPS may be too low. I recommend 120Hz at minimum. The time step must be shortened to reduce fighting between competing tire forces at a standstill.

EDIT: actually I just noticed I overlooked what you said right before posting that bit of code:
I forgot about that detail. :) Here's how I'm adding the forces with a lateral vector rotated from the surface normal:

Code:
var lateral_vector: Vector3 = normal.rotated(-global_transform.basis.y, 0.25 * TAU)
car.add_force(lateral_vector * x_force, contact)
car.add_force(normal * y_force, contact)
car.add_force(global_transform.basis.y * z_force, contact)
 
Last edited:
Actually I didn't remember when I wrote my last post, but I had just fixed the "slippery" part before writing it. I'm not quite sure what the issue was, but I think it was related to some wheel not being set up properly. I just duplicated the wheels I thought were working fine and the issue went away. So when idle the car is perfectly still. Unless I change the basis to Y axis in that code above, in which case it will wander off forward, but I guess that's due to the springiness of the suspensions after it falls from the starting height.

My physics are at 240 fps. And it seems my wheel rotations are all ok.

So I'm not sure what's wrong, but it may be related to my engine code, which is probably wrong. I tried that bit of code you just posted, but the behavior seems to be the same.

I'm unsure about the drive_torque and the brake_torque, for example. I made a constant for the torque power, and I'm multiplying that by the user input keys. I suppose the input is the throttle?

But then the left wheels keep spinning weirdly when the car is stopped, and I have that issue with the basis axis.

If I comment out the engine code, the car doesn't even stay upright, it just flips over at the start.
My brain is just exploding. :)

I have my project in a repo, if you'd like to take a look. I made it as simple as possible.
 
Last edited:
@Skaruts -- I've taken a look at the project. I found a couple mistakes, but fixing them and adding rudimentary steering input indicates that you're on the right track. My feedback:
  • The Z component of adding force to the car should use the Y basis instead of the X basis. It got mixed up in the confusion, because you had it right before. :)
  • Throttle and braking should be kept separate. Adding another set of variables for braking improved acceleration and braking so well, it revealed the next problem...
  • The test car is very top-heavy. You can change the center of gravity by moving the children of the RigidBody; I think maybe you meant to do this, but the offset is backwards, making the car easy to flip.
 
Damn! I never noticed how high my center of gravity was until now. That was way up there! :boggled:
The Z component of adding force to the car should use the Y basis instead of the X basis. It got mixed up in the confusion, because you had it right before. :)
Yea, I just reverted it to X because I was assuming the issue might be somewhere else.
But then... why is it Y for me and X for you? I don't see what's making it different.
Throttle and braking should be kept separate. Adding another set of variables for braking improved acceleration and braking so well, it revealed the next problem...
I suppose I did it right now, it's actually working. :P

Thank you so much! :cheers:


Now, the wheels are still spinning by themselves when idle, though.
 
Last edited:
Should I care about using ackermann steering if my goal is something less realistic, along the lines of Carmageddon or Interstate 76?
 
Should I care about using ackermann steering if my goal is something less realistic, along the lines of Carmageddon or Interstate 76?
It depends, between how it impacts the gameplay and how your tire model behaves. It will matter more if there is more loss of control from scrubbing the front tires, and/or if players will be negotiating many slower corners.
 
Can someone lend a hand real quick? I'm having the worst time trying to apply the longitudinal force.
For instance, the apply_torque() function returns a value, but how do I use this value? and where?
It seems like it is supposed to be a delta_torque and to convert it to linear force all I have to do is multiply by the radius of the wheel, but that gives really strange behaviour.
Also, why isn't the vehicle mass being taken into account anywhere? I had to multiply suspension force by mass in order for it to work properly with vehicles that have different mass.
I assume this would be the case for longitudinal force as well.
 
For instance, the apply_torque() function returns a value, but how do I use this value? and where?
The return value is used by the example rwd() function that calls apply_torque(). It represents resistance on the wheel, and is used to calculate the differential split in rwd().

Also, why isn't the vehicle mass being taken into account anywhere? I had to multiply suspension force by mass in order for it to work properly with vehicles that have different mass.
The vehicle's mass is accounted for by the RigidBody node that embodies the car. Heavier vehicles require stronger springs. :)
 
Thanks a lot for the reply, I still don't understand how to turn all of that into a force to be applied to the rigidbody, like, I don't see anything setting z_force appart from the pacejka function. Or a call to add_force utilizing any values.
 
@Sabudum -- The add_force() calls were first established in part III, with the tires. I intended for each part to build on the previous parts. In this model, the engine turns the wheels, and torque is converted to forward motion purely through the tire's interaction with the surface. The only thing apply_torque() needs to do is modify the virtual angular velocity, or "spin".
 
Thanks a lot for your help Wolfe, I think I understand the concept now, but I have a new problem, when I do this:

slip = (spin * radius - z_vel) / abs(z_vel)
var z_force = pacejka(slip, z_shape)

Since apply_torque() has this:

var net_torque = z_force * radius
spin += delta * net_torque / (wheel_moment + drive_inertia)

It generates a force, and this force makes the car accelerate to infinity, unless I constantly keep pressing the brakes, if I accelerate as normal the car behaves like normal, but I can't seem to null that force when the car is stopped.
Even if I'm on neutral gear or the engine is off, as the car moves a milimeter forward, then a force would be generated and keep accelerating because slip increased just a tad bit as the car moved. How did you deal with this?
 
Last edited:
@Sabudum -- If I understand you correctly, that's a mathematical consequence of the tire formula; it is undefined when the car is stopped. 😐 In apply_torque(), holding the brake helps because I've forced the wheel to quit spinning if the wheel is already spinning slowly and the braking force exceeds net_torque. That's a simplistic solution for that particular condition.

The next best solution is to increase the physics FPS. Longer time steps result in more unwanted forces because there is more time for micro-slip to occur; shorter time steps produce a cleaner balance of forces when the tire is stationary. I recommend 120Hz at minimum for either the Pacejka or Brush models.

On the in-development version of GDSim, I am pretty well satisfied with another solution -- averaging the current frame's tire forces with the previous frame, as a pair of Vector2s. It helps quell oscillations at lower physics rates without negatively affecting intended behavior.
 
Hello again! I was playing around with the code and a question popped in my head: do you calculate the net_torque and wheelspin in both apply_forces() and apply_torque() functions? I mean the code you introduced in part IV that goes like
var net_torque = z_force * radius net_torque += drive_torque if abs(spin) < 5 and brake_torque > abs(net_torque): spin = 0 else: net_torque -= brake_torque * sign(spin) spin += delta * net_torque / wheel_moment

I do calculate these in both functions, and that might be the reason i had problems with the inertias earlier. I removed these lines from apply_forces() function and the simulation got better, imo.
 
Last edited:
hello
i came here to ask about what collisions(trimesh - convex) and what physics engine you are using on Godot

im trying to make a high speed racing game (300kmh and up) and my problem is that whenever the vehicle jumps a little bit the springarm node goes under the ground
i am using the (DEFAULT) physics engine and i think its bulletphysics
and im using a trimesh collision for the track

if i use collisions with volume ( like a box collisionshape or convex) for the track surface
the vehicle starts to bounce
its as if the springarm node is going through the collisions and it tries to push back but it keeps going through again and again

im not sure if this is because the suspension isnt strong enough or the rigidbody is too heavy

if i use the godot physics these problems mostly get fixed
and now it only goes through the ground if its falling at very high speed but i fixed that by simply making the vehicle collisionshape be below the springarm origin by a little bit

tho i noticed there's some clipping issue with edges of trimesh collisions in godot physics
when the vehicle hits the edge of a trimesh surface it bounces back for some reason


with convex collisions everything seems to be fine

but yeah i just wanted to ask what you use and what works with you

because making collisions for an entire highway with convex takes quite a lot of time😅
 
@Raghid -- Hello! Actually, your results are curious to me, because I've had the opposite experience. I use Bullet by necessity, because in my experience it's Godot Physics that allows the SpringArm to punch through the ground. The Godot physics engine also causes another serious problem with a different part of the simulation in GDSim; I've been unable to get it to cooperate.

I'm afraid all I can suggest is that the suspension and tire model both benefit from a higher physics rate, preferably a minimum of 120FPS. GDSim's track colliders are all trimeshes, with "smooth trimesh collisions" enabled in the project settings. I hope that's enough to help!
if i use the godot physics these problems mostly get fixed
and now it only goes through the ground if its falling at very high speed but i fixed that by simply making the vehicle collisionshape be below the springarm origin by a little bit
Good call. I hadn't mentioned it, but it's a good idea to protect the SpringArm origin with a shape on the Rigidbody, whether it's the main shape or extras.
 
@Wolfe hi again

i did some drop tests and seems like the suspensions only work properly when im using godotphysics and springarm has no shape attached to it (so its just a raycast)

check these drop test vids

GDPHYSICS drop with no shape casting (works)

GDPHYSICS drop with shape casting


BULLET drop with no shape casting

BULLET drop with shape casting


but in the ramp test gdphysics with shape casting and bullet with and without shape casting react a bit differently...

idk check this ramp test video

oops i just noticed i made the visual wheels rotate in the wrong direction
THIS IS ONLY VISUAL it does not affect anything

im using the exact same suspension script for my game

i attached the project file incase you want to test yourself
im using godot 3.4.2
 

Attachments

  • godotphysics springarm suspension.zip
    13.4 KB · Views: 28
Last edited:
@Raghid -- Interesting! I don't remember if I had noticed before that Godot Physics will work so long as you don't use a shape. Fortunately, I have solved the problem in your project so that Bullet with a shape is an option, as it should be. :)

I changed these things:
  1. Set project physics FPS to 120
  2. Copy-paste your shape resource to the wheels
  3. Move the SpringArm origins so they are protected within a CollisionShape belonging to the RigidBody
  4. Move the ramp aside, for a hard drop
 

Attachments

  • godotphysics springarm suspension.zip
    13.9 KB · Views: 46
ah thanks!
honestly i was scared to touch the physics fps value thinking it would break the game or make its performance worse 🙃
 
ah thanks!
honestly i was scared to touch the physics fps value thinking it would break the game or make its performance worse 🙃
It does affect performance, so you want to keep it as low as possible. However, the equations we're using to calculate forces ultimately work better with at least 120FPS. 👍
 
Back