Car Controller - Day 2


Wheel Collider

The first video I watched was (Simple Car Controller in Unity Tutorial) which creates a car controller using the Wheel Colliders. It functions by setting the motorTorque, brakeTorque and steerAngle of the wheel. This controller works fine, but doesn't give me much insight, since the real mechanics are hidden within the wheel collider.

 Another video, which was probably better (I only skimmed through it) was here, this went into more details . 


The settings of the wheel collider does look difficult and might need a lot of tweaking to get the "correct" numbers. I did find a video that linked to an EasySuspension code. This allows an easy way to update the wheel collider values.


Cuboid as the moving object - using suspensions

I saw this video which was interesting, as it introduced me to the idea of a cube being moved around. suspensions and drifting. However the video was not practically helpful to me as no code was shared. I watched the video that it referenced, which helped me understand how the concept of the suspension would be implemented.

I also saw this video which was really educational, a well made video. It had snippets of code so between all these sources I thought I should be able to get something working and have some understanding of the physics formulas involved. Unfortunately time being the factor I wasn't able to pursue this direction for long enough.

I got as far as having the object float and move forward - the code is given in Appendix b


Sphere as the moving object

The next video I saw used a sphere for the physics and then moved the car to match the spheres' position. I then watched another video that did the same thing.


Conclusion

I think using hand coded suspensions would've been the best route from a pure understanding point of view. Next best would've been to use the wheel collider, perhaps with a helper class of some sort. Both these methods give a more arcade bouncy feel and allow the wheels to move independently, or at least look like it.

The method I settled on using (given in Appendix A) was having a sphere roll around and the car "skin" follow it. This way I have a decent feel without lots of number tweaking, but I do have some numbers I can modify.

I also added a particle effect for smoke when the car moves. Line trail for the tire marks and a simple explosion effect when it hits an object.

I couldn't find any code highlighter that would convert this to something itch.io blog would accept. So here it is in a raw form

Resources

Appendix A - moving a sphere

using UnityEngine; public class CarController_v2 : MonoBehaviour { public Rigidbody theRB; public float forwardAccel = 8f; // Forward acceleration public float reverseAccel = 4f; // Reverse acceleration public float maxSpeed = 50f; public float turnStrength = 180; // how quickly we can turn public float gravitryForce = 10f; // When in the air, how much force is pulling the car down back to earth public float dragOnGround = 3f; // How much drag when on the ground public float dragInAir = .1f; // How much drag when we're int he air private float speedInput, turnInput; [Header("Ground Check")] private bool isGrounded; // flag - are we on the ground public LayerMask groundLayer; // defines what layer is regarded as the ground public float groundRayLength = .5f; // how long ther ground-check ray is public Transform groundRaypoint; // where in space we want our ray to be cast from public float alighToGroundTime = 5f; [Header("Wheels")] public Transform WheelFL; public Transform WheelFR; public float maxWheelTurn = 25f; [Header("Particle effects")] public ParticleSystem[] dustTrailEffet; public float dustTrailMaxEmissions = 25f; private float dustTrailEmissionRate; // TODO: Tire burn marks public TrailRenderer[] tireTrails; void Start() { // remove the sphere from being part of the car theRB.transform.parent = null; } void Update() { // get inputs var inputVert = Input.GetAxis("Vertical"); var inputHorz = Input.GetAxis("Horizontal"); // Are we going forward to backward speedInput = (inputVert > 0 ? forwardAccel : reverseAccel) * inputVert * 1000f; // turning amount turnInput = inputHorz; // rotate object if (isGrounded) // Only turn when on the ground transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles + new Vector3(0f , turnInput * turnStrength * Time.deltaTime * inputVert, 0f)); // turn front wheels //WheelFL.localRotation = Quaternion.Euler(WheelFL.localRotation.eulerAngles.x, (turnInput * maxWheelTurn) - 180, WheelFL.localRotation.eulerAngles.z); // Might need to rotate wheel by 180, depending on how the car wheels are setup WheelFL.localRotation = Quaternion.Euler(WheelFL.localRotation.eulerAngles.x, (turnInput * maxWheelTurn), WheelFL.localRotation.eulerAngles.z); WheelFR.localRotation = Quaternion.Euler(WheelFR.localRotation.eulerAngles.x, (turnInput * maxWheelTurn) , WheelFR.localRotation.eulerAngles.z); // set position transform.position = theRB.transform.position; // Ground check RaycastHit hit; isGrounded = Physics.Raycast(groundRaypoint.position, -transform.up, out hit, groundRayLength, groundLayer); // Smoothly rotate object to alight to ground Quaternion toRotateTo = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation; transform.rotation = Quaternion.Slerp(transform.rotation, toRotateTo, alighToGroundTime * Time.deltaTime); // Update drag theRB.linearDamping = isGrounded ? dragOnGround : dragInAir; // // Effects // // initial value of dust effect dustTrailEmissionRate = 0f; foreach (var trail in tireTrails) trail.emitting = false; // set value for tire trail and dust effect if (isGrounded && Mathf.Abs(speedInput) > 0) { dustTrailEmissionRate = dustTrailMaxEmissions; foreach (var trail in tireTrails) trail.emitting = true; } // setup the dust effect foreach (var trailParticleSystem in dustTrailEffet) { var emissionModule = trailParticleSystem.emission; emissionModule.rateOverTime = dustTrailEmissionRate; } } private void FixedUpdate() { if (isGrounded) { // move car using AddForce if (Mathf.Abs(speedInput) > 0) { theRB.AddForce(transform.forward * speedInput); } } else { // Add extra gravity push downwards theRB.AddForce(Vector3.up * -gravitryForce * 100 , ForceMode.Force); } } }

Appendix B - floating cuboid with suspensions

using UnityEngine; // ref: https://www.youtube.com/watch?v=CdPYlj5uZeI public class testSuspension : MonoBehaviour { public Rigidbody carRB; public Transform[] tires; public float suspensionRestDist; public float springStrength; public float springDamper; public LayerMask groundLayer; public float rayLength = 1f; public float forwardAccel = 8f; // Forward acceleration public float reverseAccel = 4f; // Reverse acceleration void Start() { } void Update() { var inputVert = Input.GetAxis("Vertical"); float speedInput = (inputVert > 0 ? forwardAccel : reverseAccel) * inputVert * 1000f; carRB.AddForce(transform.forward * speedInput); } void FixedUpdate() { // If grounded foreach (Transform t in tires) ApplyForce(t); } public void ApplyForce(Transform tireTransform) { RaycastHit tireRay; var rayDidHit = Physics.Raycast(tireTransform.position, Vector3.down, out tireRay, rayLength, groundLayer); if (!rayDidHit) return; // worldspace direction of the spring force Vector3 springDir = tireTransform.up; Vector3 tireWorldVel = carRB.GetPointVelocity(tireTransform.position); // calculate offset from raycast float offset = suspensionRestDist - tireRay.distance; float vel = Vector3.Dot(springDir, tireWorldVel); float force = (offset * springStrength) - (vel * springDamper); // apply the force at the location of this tuire, in the direction of the suspension carRB.AddForceAtPosition(springDir * force, tireTransform.position); } }

I used the following values:


Leave a comment

Log in with itch.io to leave a comment.