Untitled

custom car control
mail@pastecode.io avatar
unknown
csharp
a year ago
6.0 kB
4
Indexable
Never
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class CustomCarPhysicControl : MonoBehaviour
{
    private Rigidbody rb;
    [SerializeField] Transform []tires;

    [Header("Suspension")]
    [SerializeField] private float offset = 0.4f;
    [SerializeField] private float springForce = 100;
    [SerializeField] private float dampingForce = 10;

    [Header("Accleration")]
    [SerializeField] private float carTopSpeed = 10;
    [SerializeField] private AnimationCurve powerCurve;
    public float accelerationInput;

    [Header("Steering")]
    [SerializeField] private float tireMass = 2;
    [SerializeField] [Range(0, 1)] private float gripFactor = 0.4f;
    //[SerializeField] AnimationCurve gripFactor;
    [SerializeField] private float maxTurnTire = 45;
    public float currentTurn;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        Inputs();
        WheelDisplay();
    }

    private void FixedUpdate()
    {
        Acceleration();
        Steering();
        Suspension();
    }

    void Inputs()
    {
        currentTurn += Input.GetAxis("Horizontal") * 0.5f;
        currentTurn = Mathf.Clamp(currentTurn, -maxTurnTire, maxTurnTire);
        accelerationInput = Input.GetAxis("Vertical");
    }

    void Acceleration()
    {
        RaycastHit hit;

        for (int i = 0; i < tires.Length; i++)
        {
            if (Physics.Raycast(tires[i].position, Vector3.down, out hit, offset))
            {
                //world-space direction of the acceleration/braking force.
                Vector3 accelerationDirection = tires[i].forward;

                //acceleration torgue
                if(accelerationInput > 0.0f)
                {
                    //forwar speed of the car (in the firection of driving)
                    float carSpeed = Vector3.Dot(transform.forward, rb.velocity);

                    //normalized car speed
                    float normalizedSpeed = Mathf.Clamp01(Mathf.Abs(carSpeed) / carTopSpeed);

                    //available torque
                    float availableTorque = powerCurve.Evaluate(normalizedSpeed) * accelerationInput;

                    rb.AddForceAtPosition(accelerationDirection * availableTorque, tires[i].position);
                }

            }
        }
    }

    void Steering()
    {
        RaycastHit hit;

        for (int i = 0; i < tires.Length; i++)
        {
            if (Physics.Raycast(tires[i].position, Vector3.down, out hit, offset))
            {
                //world space direction of the spring force.
                Vector3 steeringDirection = tires[i].right;

                //wold space velocity of this tire.
                Vector3 tireWorldVelocity = rb.GetPointVelocity(tires[i].position);

                //what it's the tire's velocit in the steering direction?
                //note that steeringDirection is a unit vector, so this returns thje magnitude of tireworldVelocity
                //as projected onto steeringDirection
                float steeringVelocity = Vector3.Dot(steeringDirection, tireWorldVelocity);

                //the change in velocity that we're looking for is -steeringVelocity * gripFactor
                //gripFactor is in range 0-1, 0 means NO grip, 1 means full grip
                float desiredVelocityChange = -steeringVelocity * gripFactor;

                //turn change in velocity into an acceleration (accleration = change in velocity / time )
                //this will produce the acceleration necessary to change the velocity by desiredVelocityChange in 1 physics step
                float desiredAcceleration = desiredVelocityChange / Time.fixedDeltaTime;

                //Force = Mass * Acceleration, so multiply by the mass of the tire and as a force.
                rb.AddForceAtPosition(steeringDirection * tireMass * desiredAcceleration, tires[i].position);
            }
        }
    }

    void Suspension()
    {
        RaycastHit hit;

        for (int i = 0; i < tires.Length; i++)
        {
            if(Physics.Raycast(tires[i].position, Vector3.down, out hit, offset))
            {
                //world space direction of the spring force.
                Vector3 springDirection = tires[i].up;

                //wold space velocity of this tire.
                Vector3 tireWorldVelocity = rb.GetPointVelocity(tires[i].position);

                //calculate offset from the raycast ( suspensionRestDist - tireRay.distance )
                float raycastOffset = offset - hit.distance;

                //calculate velocity along the spring direction
                //note that springDir is a unit Vector, so this returns the magnitude of tireWorldVelocity
                float springVelocity = Vector3.Dot(springDirection, tireWorldVelocity);

                //calculate the magnitude of the dampened spring force.
                float force = (raycastOffset * springForce) - (springVelocity * dampingForce);

                //apply the force at the location of this tire, in the direction of suspension
                rb.AddForceAtPosition(springDirection * force, tires[i].position);
            }
        }
    }

    void WheelDisplay()
    {
        if (tires.Length > 0)
        {
            tires[0].transform.localRotation = Quaternion.Euler(0f, currentTurn, 0f);
            tires[1].transform.localRotation = Quaternion.Euler(0f, currentTurn, 0f);
        }
    }

    private void OnDrawGizmos()
    {
        if (tires.Length > 0)
        {
            for (int i = 0; i < tires.Length; i++)
            {
                Gizmos.DrawRay(tires[i].position, Vector3.down * offset);
            }
        }
    }
}