Skip to content

Instantly share code, notes, and snippets.

@nothke
Last active August 12, 2021 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nothke/bc3b6aee1aaa4f0825bc2908ce318913 to your computer and use it in GitHub Desktop.
Save nothke/bc3b6aee1aaa4f0825bc2908ce318913 to your computer and use it in GitHub Desktop.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CarTorque : MonoBehaviour
{
//// Inspector
[Header("Geometry")]
public float wheelbase = 1;
public float trackWidth = 1;
[Header("Handling")]
// Turning to velocity multipliers:
public float sidewaysDragMult = 1;
public float yawDragMult = 1;
// Keep curves' y between 0-1 and use the multipliers for vertical scaling instead:
public float torqueVelocityMult = 1;
public AnimationCurve torqueByVelocityCurve = new AnimationCurve(new[] { new Keyframe(0, 0), new Keyframe(100, 1) });
public float thrustVelocityMult = 1;
public AnimationCurve thrustByVelocityCurve = new AnimationCurve(new[] { new Keyframe(0, 1), new Keyframe(30, 0) });
[Header("Suspension")]
public float springRate = 10;
public float damperRate = 1; // Optimal damper rate should always be ~1/10 of spring rate (not too stiff, not too bouncy). I don't know why exactly, it's the magic of the universe, it's true even in real cars :P
public float raycastDistance = 1; // ride height
//// private
Rigidbody _rb;
Rigidbody rb { get { if (!_rb) _rb = GetComponent<Rigidbody>(); return _rb; } }
Vector3[] wheelPoints;
private void Start()
{
wheelPoints = new Vector3[4];
wheelPoints[0] = new Vector3(-trackWidth * 0.5f, 0, wheelbase * 0.5f); // FL
wheelPoints[1] = new Vector3(trackWidth * 0.5f, 0, wheelbase * 0.5f); // FR
wheelPoints[2] = new Vector3(-trackWidth * 0.5f, 0, -wheelbase * 0.5f); // RL
wheelPoints[3] = new Vector3(trackWidth * 0.5f, 0, -wheelbase * 0.5f); // RR
}
private void FixedUpdate()
{
bool grounded = false;
// Raycast from each wheel point, check grounding and push with suspension
for (int i = 0; i < wheelPoints.Length; i++)
{
Vector3 wp = transform.TransformPoint(wheelPoints[i]);
if (Physics.Raycast(wp, -transform.up, out RaycastHit hit, raycastDistance))
{
grounded = true;
// Spring force is proportional to compression
float spring = (raycastDistance - hit.distance) * springRate;
// Damping force is proportional to vertical wheel velocity, clamped to prevent crazy values
Vector3 veloAtWheel = rb.GetPointVelocity(wp);
Vector3 relVeloAtWheel = transform.InverseTransformDirection(veloAtWheel);
float verticalRelVeloAtWheel = relVeloAtWheel.y;
float damp = -Mathf.Clamp(verticalRelVeloAtWheel, -2, 2) * damperRate;
rb.AddForceAtPosition(hit.normal * (spring + damp), wp);
}
}
if (grounded)
{
var velo = rb.velocity;
var localVelo = transform.InverseTransformDirection(velo);
float sidewaysVelo = localVelo.x;
float forwardVelo = localVelo.z;
float torque = torqueByVelocityCurve.Evaluate(Mathf.Abs(sidewaysVelo)) * Mathf.Sign(sidewaysVelo) * torqueVelocityMult;
Vector3 localAngVelo = transform.InverseTransformDirection(rb.angularVelocity);
float yawRate = localAngVelo.y;
float yawDrag = -Mathf.Clamp(yawRate, -1, 1) * yawDragMult;
rb.AddRelativeTorque(0, torque + yawDrag, 0);
float thrust = thrustByVelocityCurve.Evaluate(Mathf.Clamp(forwardVelo, 0, Mathf.Infinity)) * forwardVelo * thrustVelocityMult;
rb.AddRelativeForce(-sidewaysVelo * sidewaysDragMult, 0, thrust);
}
}
// Draws lines for each wheel so you can set up wheelbase and track easily
private void OnDrawGizmosSelected()
{
Vector3 fl = new Vector3(-trackWidth * 0.5f, 0, wheelbase * 0.5f); // FL
Vector3 fr = new Vector3(trackWidth * 0.5f, 0, wheelbase * 0.5f); // FR
Vector3 rl = new Vector3(-trackWidth * 0.5f, 0, -wheelbase * 0.5f); // RL
Vector3 rr = new Vector3(trackWidth * 0.5f, 0, -wheelbase * 0.5f); // RR
Gizmos.DrawRay(transform.TransformPoint(fl), -transform.up * raycastDistance);
Gizmos.DrawRay(transform.TransformPoint(fr), -transform.up * raycastDistance);
Gizmos.DrawRay(transform.TransformPoint(rl), -transform.up * raycastDistance);
Gizmos.DrawRay(transform.TransformPoint(rr), -transform.up * raycastDistance);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment