Godot Version
4.2.stable.mono
Question
I am creating a script to do custom vehicle suspension. The script applies an upward force at each wheel position according to the offset of the collision point to the rest distance, the current velocity of the wheel, the spring strength, and the damping strength.
The issue is that when the vehicle is rotated to some degree (I do this by applying an angular force), the forces applied seem to oscillate as if it looses balance.
I’m not sure what the problem is here. I am unfamiliar with Godot’s physics engine and pretty new to Godot in general.
Vehicle.cs
using Godot;
using System;
namespace Vehicles
{
public partial class Vehicle : RigidBody3D
{
Wheel fl; public Wheel FL { get => fl; }
Wheel fr; public Wheel FR { get => fr; }
Wheel rl; public Wheel RL { get => rl; }
Wheel rr; public Wheel RR { get => rr; }
[Export] float wheel_base; // front wheels distance to rear wheels
[Export] float track_width; // left wheels distance to right wheels
[Export] float max_torque = 100;
[Export] Curve torqueCurve;
// Suspension variables
[Export] float restdistance;
public float RestDistance { get => restdistance; }
[Export] float spring_strength;
public float SpringStrength { get => spring_strength; }
[Export] float damping_strength;
public float DampingStrength { get => damping_strength; }
public override void _Ready()
{
fl = (Wheel)GetNode("FrontLeftWheel");
fr = (Wheel)GetNode("FrontRightWheel");
rl = (Wheel)GetNode("RearLeftWheel");
rr = (Wheel)GetNode("RearRightWheel");
SetRestDistance(restdistance);
SetWheelBase(wheel_base);
SetTrackWidth(track_width);
}
public override void _PhysicsProcess(double delta)
{
if (fl.RayCast3D.IsColliding())
{
AddSuspensionForce(fl, (float)delta);
}
if (fr.RayCast3D.IsColliding())
{
AddSuspensionForce(fr, (float)delta);
}
if (rl.RayCast3D.IsColliding())
{
AddSuspensionForce(rl, (float)delta);
}
if (rr.RayCast3D.IsColliding())
{
AddSuspensionForce(rr, (float)delta);
}
}
void AddSuspensionForce(Wheel wheel, float delta)
{
float offset = restdistance - wheel.GlobalPosition.DistanceTo(wheel.RayCast3D.GetCollisionPoint());
wheel.TireVel = GetWheelVelocity(wheel, delta);
wheel.SpringVel = GlobalBasis.Y.Dot(wheel.TireVel);
wheel.SuspensionForce = (offset * spring_strength) - (wheel.SpringVel * damping_strength);
ApplyForce(GlobalBasis.Y * wheel.SuspensionForce, wheel.Position);
}
Vector3 GetWheelVelocity(Wheel wheel, float delta)
{
Vector3 velocity = (wheel.GlobalPosition - wheel.PreviousPosition) / delta;
wheel.PreviousPosition = wheel.GlobalPosition;
return velocity;
}
public void SetWheelBase(float newWheelBase)
{
wheel_base = newWheelBase;
fl.Position = new Vector3(fl.Position.X, fl.Position.Y, wheel_base / 2);
fr.Position = new Vector3(fr.Position.X, fr.Position.Y, wheel_base / 2);
rl.Position = new Vector3(rl.Position.X, rl.Position.Y, -(wheel_base / 2));
rr.Position = new Vector3(rr.Position.X, rr.Position.Y, -(wheel_base / 2));
}
public void SetTrackWidth(float newTrackWidth)
{
track_width = newTrackWidth;
fl.Position = new Vector3(-(track_width / 2), fl.Position.Y, fl.Position.Z);
fr.Position = new Vector3(track_width / 2, fr.Position.Y, fr.Position.Z);
rl.Position = new Vector3(-(track_width / 2), rl.Position.Y, rl.Position.Z);
rr.Position = new Vector3(track_width / 2, rr.Position.Y, rr.Position.Z);
}
public void SetRestDistance(float newRestDistance)
{
restdistance = newRestDistance;
fl.RayCast3D.TargetPosition = new Vector3(0, -restdistance, 0);
fr.RayCast3D.TargetPosition = new Vector3(0, -restdistance, 0);
rl.RayCast3D.TargetPosition = new Vector3(0, -restdistance, 0);
rr.RayCast3D.TargetPosition = new Vector3(0, -restdistance, 0);
}
}
}