Files
CartoonCopChase/Assets/Ash Assets/Arcade Vehicle Physics/Scripts/ArcadeVehicleController.cs
2026-08-28 16:09:06 +01:00

288 lines
10 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace ArcadeVP
{
public class ArcadeVehicleController : MonoBehaviour
{
public enum GroundCheckType { RayCast, SphereCast };
public enum MovementMode { Velocity, AngularVelocity };
public MovementMode movementMode;
[FormerlySerializedAs("GroundCheck")]
public GroundCheckType groundCheck;
public LayerMask drivableSurface;
[FormerlySerializedAs("MaxSpeed")]
public float maxSpeed;
[FormerlySerializedAs("accelaration")]
public float acceleration;
public float turn;
public float gravity = 7f;
public float downforce = 5f;
[FormerlySerializedAs("AirControl")]
[Tooltip("if true : can turn vehicle in air")]
public bool airControl = false;
[Tooltip("if true : vehicle will drift instead of brake while holding space")]
public bool kartLike = false;
[Tooltip("turn more while drifting (while holding space) only if kart Like is true")]
public float driftMultiplier = 1.5f;
public Rigidbody rb;
public Rigidbody carBody;
[HideInInspector]
public RaycastHit hit;
public AnimationCurve frictionCurve;
public AnimationCurve turnCurve;
public PhysicsMaterial frictionMaterial;
[Header("Visuals")]
[FormerlySerializedAs("BodyMesh")]
public Transform bodyMesh;
[FormerlySerializedAs("FrontWheels")]
public Transform[] frontWheels = new Transform[2];
[FormerlySerializedAs("RearWheels")]
public Transform[] rearWheels = new Transform[2];
[HideInInspector]
public Vector3 carVelocity;
[Range(0, 10)]
[FormerlySerializedAs("BodyTilt")]
public float bodyTilt;
[Header("Audio settings")]
public AudioSource engineSound;
[Range(0, 1)]
public float minPitch;
[Range(1, 3)]
[FormerlySerializedAs("MaxPitch")]
public float maxPitch;
[FormerlySerializedAs("SkidSound")]
public AudioSource skidSound;
[HideInInspector]
public float skidWidth;
private float radius, steeringInput, accelerationInput, brakeInput;
private Vector3 origin;
private bool isGrounded;
private void Start()
{
radius = rb.GetComponent<SphereCollider>().radius;
if (movementMode == MovementMode.AngularVelocity)
{
Physics.defaultMaxAngularSpeed = 100;
}
}
private void Update()
{
Visuals();
AudioManager();
}
public void ProvideInputs(float _steeringInput, float _accelerationInput, float _brakeInput)
{
steeringInput = _steeringInput;
accelerationInput = _accelerationInput;
brakeInput = _brakeInput;
}
public void AudioManager()
{
engineSound.pitch = Mathf.Lerp(minPitch, maxPitch, Mathf.Abs(carVelocity.z) / maxSpeed);
if (Mathf.Abs(carVelocity.x) > 10 && grounded())
{
skidSound.mute = false;
}
else
{
skidSound.mute = true;
}
}
private void FixedUpdate()
{
UpdateGroundedState();
carVelocity = carBody.transform.InverseTransformDirection(carBody.linearVelocity);
if (Mathf.Abs(carVelocity.x) > 0)
{
frictionMaterial.dynamicFriction = frictionCurve.Evaluate(Mathf.Abs(carVelocity.x / 100));
}
if (grounded())
{
float sign = Mathf.Sign(carVelocity.z);
float TurnMultiplyer = turnCurve.Evaluate(carVelocity.magnitude / maxSpeed);
if (kartLike && brakeInput > 0.1f)
{
TurnMultiplyer *= driftMultiplier;
}
if (accelerationInput > 0.1f || carVelocity.z > 1)
{
carBody.AddTorque(Vector3.up * steeringInput * sign * turn * 100 * TurnMultiplyer);
}
else if (accelerationInput < -0.1f || carVelocity.z < -1)
{
carBody.AddTorque(Vector3.up * steeringInput * sign * turn * 100 * TurnMultiplyer);
}
if (!kartLike)
{
if (brakeInput > 0.1f)
{
rb.constraints = RigidbodyConstraints.FreezeRotationX;
}
else
{
rb.constraints = RigidbodyConstraints.None;
}
}
if (movementMode == MovementMode.AngularVelocity)
{
if (Mathf.Abs(accelerationInput) > 0.1f && brakeInput < 0.1f && !kartLike)
{
rb.angularVelocity = Vector3.Lerp(rb.angularVelocity, carBody.transform.right * accelerationInput * maxSpeed / radius, acceleration * Time.deltaTime);
}
else if (Mathf.Abs(accelerationInput) > 0.1f && kartLike)
{
rb.angularVelocity = Vector3.Lerp(rb.angularVelocity, carBody.transform.right * accelerationInput * maxSpeed / radius, acceleration * Time.deltaTime);
}
}
else if (movementMode == MovementMode.Velocity)
{
if (Mathf.Abs(accelerationInput) > 0.1f && brakeInput < 0.1f && !kartLike)
{
rb.linearVelocity = Vector3.Lerp(rb.linearVelocity, carBody.transform.forward * accelerationInput * maxSpeed, acceleration / 10 * Time.deltaTime);
}
else if (Mathf.Abs(accelerationInput) > 0.1f && kartLike)
{
rb.linearVelocity = Vector3.Lerp(rb.linearVelocity, carBody.transform.forward * accelerationInput * maxSpeed, acceleration / 10 * Time.deltaTime);
}
}
rb.AddForce(-transform.up * downforce * rb.mass);
carBody.MoveRotation(Quaternion.Slerp(carBody.rotation, Quaternion.FromToRotation(carBody.transform.up, hit.normal) * carBody.transform.rotation, 0.12f));
}
else
{
if (airControl)
{
float TurnMultiplyer = turnCurve.Evaluate(carVelocity.magnitude / maxSpeed);
carBody.AddTorque(Vector3.up * steeringInput * turn * 100 * TurnMultiplyer);
}
carBody.MoveRotation(Quaternion.Slerp(carBody.rotation, Quaternion.FromToRotation(carBody.transform.up, Vector3.up) * carBody.transform.rotation, 0.02f));
rb.linearVelocity = Vector3.Lerp(rb.linearVelocity, rb.linearVelocity + Vector3.down * gravity, Time.deltaTime * gravity);
}
}
public void Visuals()
{
foreach (Transform FW in frontWheels)
{
FW.localRotation = Quaternion.Slerp(FW.localRotation, Quaternion.Euler(FW.localRotation.eulerAngles.x,
30 * steeringInput, FW.localRotation.eulerAngles.z), 0.7f * Time.deltaTime / Time.fixedDeltaTime);
FW.GetChild(0).localRotation = rb.transform.localRotation;
}
rearWheels[0].localRotation = rb.transform.localRotation;
rearWheels[1].localRotation = rb.transform.localRotation;
if (carVelocity.z > 1)
{
bodyMesh.localRotation = Quaternion.Slerp(bodyMesh.localRotation, Quaternion.Euler(Mathf.Lerp(0, -5, carVelocity.z / maxSpeed),
bodyMesh.localRotation.eulerAngles.y, bodyTilt * steeringInput), 0.4f * Time.deltaTime / Time.fixedDeltaTime);
}
else
{
bodyMesh.localRotation = Quaternion.Slerp(bodyMesh.localRotation, Quaternion.Euler(0, 0, 0), 0.4f * Time.deltaTime / Time.fixedDeltaTime);
}
if (kartLike)
{
if (brakeInput > 0.1f)
{
bodyMesh.parent.localRotation = Quaternion.Slerp(bodyMesh.parent.localRotation,
Quaternion.Euler(0, 45 * steeringInput * Mathf.Sign(carVelocity.z), 0),
0.1f * Time.deltaTime / Time.fixedDeltaTime);
}
else
{
bodyMesh.parent.localRotation = Quaternion.Slerp(bodyMesh.parent.localRotation,
Quaternion.Euler(0, 0, 0),
0.1f * Time.deltaTime / Time.fixedDeltaTime);
}
}
}
private void UpdateGroundedState()
{
origin = rb.position + radius * Vector3.up;
Vector3 direction = -transform.up;
float maxdistance = radius + 0.2f;
if (groundCheck == GroundCheckType.RayCast)
{
isGrounded = Physics.Raycast(rb.position, Vector3.down, out hit, maxdistance, drivableSurface);
}
else if (groundCheck == GroundCheckType.SphereCast)
{
isGrounded = Physics.SphereCast(origin, radius + 0.1f, direction, out hit, maxdistance, drivableSurface);
}
else
{
isGrounded = false;
}
}
public bool grounded()
{
return isGrounded;
}
private void OnDrawGizmos()
{
if (rb == null || Application.isPlaying) return;
SphereCollider sphere = rb.GetComponent<SphereCollider>();
if (sphere == null) return;
float rad = sphere.radius;
float width = 0.02f;
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube(rb.transform.position + ((rad + width) * Vector3.down), new Vector3(2 * rad, 2 * width, 4 * rad));
BoxCollider box = GetComponent<BoxCollider>();
if (box != null)
{
Gizmos.color = Color.green;
Gizmos.DrawWireCube(transform.position, box.size);
}
}
}
}