You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Driftology/Assets/Scripts/VehicleController.cs

557 lines
21 KiB
C#

//using UnityEngine;
//using CnControls;
//public enum GroundCheck
//{
// RayCast,
// SphereCaste
//}
//public class VehicleController : MonoBehaviour
//{
// // Existing serialized and private fields...
// // (unchanged fields)
// [SerializeField] float _currentSpeed = 0f;
// [SerializeField] float _turnAI = 0f;
// [SerializeField] bool _isAI = false;
// [SerializeField] GroundCheck _groundCheck = GroundCheck.RayCast;
// [SerializeField] LayerMask _drivableSurface = -1;
// [SerializeField] float _maxSpeed = 100f;
// [SerializeField] float _normalAccelaration = 10f;
// [SerializeField] float _steering = 10f;
// [SerializeField] float _gravity = 9.8f;
// [SerializeField] float _downforce = 5f;
// [SerializeField] float _brakeAngle = 30f;
// [SerializeField] float _stoppingDistance = 5f;
// [SerializeField] bool _airControl = false;
// [SerializeField] AnimationCurve _frictionCurve = new AnimationCurve();
// [SerializeField] AnimationCurve _turnCurve = new AnimationCurve();
// [SerializeField, Range(0, 10)] float _bodyTilt = 0.5f;
// [SerializeField] Transform _bodyMesh = null;
// [SerializeField, Range(0.1f, 1f)] float _skidWidth = 0.25f;
// [SerializeField, Range(0.1f, 10f)] float _skidTime = 5f;
// [SerializeField] Transform _wheelFL = null;
// [SerializeField] Transform _wheelFR = null;
// [SerializeField] Transform _wheelRL = null;
// [SerializeField] Transform _wheelRR = null;
// [SerializeField] Transform _wheelFLAxel = null;
// [SerializeField] Transform _wheelFRAxel = null;
// [SerializeField] GameObject speedLines = null;
// [SerializeField] Rigidbody _sphereRB = null;
// [SerializeField] PhysicMaterial _frictionMaterial = null;
// [SerializeField] Transform _followTarget = null;
// // Internal variables
// private bool _canRun = false;
// private bool _grounded = false;
// private bool _turboPlayed = true;
// public bool _useNos = false;
// private float _speed = 0f;
// private float _brake = 0f;
// private float _accelaration = 0f;
// private float _sign = 0f;
// private float _horizontalInput = 0f;
// private float _verticalInput = 0f;
// private float _radius = 0f;
// private float _desiredTurning = 0f;
// private float _reachedTargetDistance = 0f;
// private float _distToTarget = 0f;
// private float _dot = 0f;
// private float _angleToMove = 0f;
// private float _maxDistance = 0f;
// private float _counterSteering = 0f;
// private float _turnMultiplyer = 0f;
// private Vector3 _carVelocity = Vector3.zero;
// private Vector3 _myDir = Vector3.zero;
// private Vector3 _aimedDir = Vector3.zero;
// private Vector3 _aimedPoint = Vector3.zero;
// private Vector3 _dirToMovePos = Vector3.zero;
// private RaycastHit _hit;
// private Rigidbody _carBody = null;
// private VehicleTracker _vehicleTracker = null;
// public bool IsAI => _isAI;
// public bool CanRun => _canRun;
// public bool IsGrounded => _grounded;
// public float SkidWidth => _skidWidth;
// public float SkidTime => _skidTime;
// public float Speed => _carVelocity.z;
// public float MaxSpeed => _maxSpeed;
// public bool IsTouchDown => _horizontalInput != 0 || _verticalInput != 0 || _useNos;
// public NOSController nosController;
// internal float Steering
// {
// get => _steering;
// set => _steering = value;
// }
// public Vector3 CarVelocity => _carVelocity;
// public Transform FollowTarget => _followTarget;
// [Range(0.3f, 1f)] public float steeringFactor = 1f;
// private void Awake()
// {
// _carBody = GetComponent<Rigidbody>();
// _vehicleTracker = GetComponent<VehicleTracker>();
// _radius = _sphereRB.GetComponent<SphereCollider>().radius;
// nosController = GetComponent<NOSController>();
// _maxDistance = _radius + 0.5f;
// StartVehicle();
// }
// public void StartVehicle()
// {
// _canRun = true;
// if (!_isAI)
// _accelaration = _normalAccelaration;
// }
// public void RaceFinish()
// {
// _canRun = false;
// _carVelocity = Vector3.zero;
// _currentSpeed = 0f;
// _turnAI = 0f;
// }
// private void Update()
// {
// if (!_canRun) return;
// Visuals();
// Inputs();
// Controls();
// }
// private void Inputs()
// {
// _verticalInput = CnInputManager.GetAxis("Vertical");
// _horizontalInput = CnInputManager.GetAxis("Horizontal");
// }
// private void Visuals()
// {
// _counterSteering = (Mathf.Abs(_carVelocity.x) > 20f) ? -1f : 1f;
// if (_wheelFL) _wheelFL.localRotation = Quaternion.Slerp(_wheelFL.localRotation, Quaternion.Euler(0, 45f * _counterSteering * _turnAI, 0), 0.1f);
// if (_wheelFR) _wheelFR.localRotation = Quaternion.Slerp(_wheelFR.localRotation, Quaternion.Euler(0, 45f * _counterSteering * _turnAI, 0), 0.1f);
// _wheelFLAxel?.Rotate(Vector3.right * (Speed * 0.75f));
// _wheelFRAxel?.Rotate(Vector3.right * (Speed * 0.75f));
// _wheelRL?.Rotate(Vector3.right * (Speed * 0.75f));
// _wheelRR?.Rotate(Vector3.right * (Speed * 0.75f));
// if (_bodyMesh)
// {
// if (_carVelocity.z > 1)
// _bodyMesh.localRotation = Quaternion.Slerp(_bodyMesh.localRotation,
// Quaternion.Euler(Mathf.Lerp(0, -5, _carVelocity.z / _maxSpeed), 0, Mathf.Clamp(_desiredTurning * _turnAI, -_bodyTilt, _bodyTilt)), 0.05f);
// else
// _bodyMesh.localRotation = Quaternion.Slerp(_bodyMesh.localRotation, Quaternion.identity, 0.05f);
// }
// if (speedLines)
// speedLines.SetActive(_carVelocity.z > 10);
// }
// private void Controls()
// {
// _aimedPoint = _vehicleTracker.TargetPos;
// _aimedPoint.y = transform.position.y;
// _aimedDir = (_aimedPoint - transform.position).normalized;
// _myDir = transform.forward;
// _myDir.Normalize();
// _desiredTurning = Mathf.Abs(Vector3.Angle(_myDir, Vector3.ProjectOnPlane(_aimedDir, transform.up)));
// _distToTarget = Vector3.Distance(transform.position, _vehicleTracker.TargetPos);
// _dirToMovePos = (_vehicleTracker.TargetPos - transform.position).normalized;
// _dot = Vector3.Dot(transform.forward, _dirToMovePos);
// _angleToMove = Vector3.Angle(transform.forward, _dirToMovePos);
// // ✨ No more full brake; allow drift
// _brake = 0f;
// if (_distToTarget > _reachedTargetDistance)
// {
// _speed = _dot > 0f ? _verticalInput : (_distToTarget > 5f ? _verticalInput : -1f);
// _turnAI = (Vector3.SignedAngle(transform.forward, _dirToMovePos, Vector3.up) > 0f ? 1f : -1f)
// * _turnCurve.Evaluate(_desiredTurning / 160f);
// }
// else
// {
// _turnAI = 0f;
// }
// if (!_isAI)
// _turnAI += _horizontalInput;
// _currentSpeed = Mathf.Round(Speed);
// }
// private void FixedUpdate()
// {
// if (!_canRun) return;
// Grounded();
// Movement();
// }
// private void Grounded()
// {
// if (_groundCheck == GroundCheck.RayCast)
// {
// _grounded = Physics.Raycast(_sphereRB.position, Vector3.down, out _hit, _maxDistance, _drivableSurface)
// || Physics.Raycast(_sphereRB.position + Vector3.forward * 3f, Vector3.down, out _hit, _maxDistance, _drivableSurface)
// || Physics.Raycast(_sphereRB.position - Vector3.forward * 3f, Vector3.down, out _hit, _maxDistance, _drivableSurface);
// }
// else if (_groundCheck == GroundCheck.SphereCaste)
// {
// _grounded = Physics.SphereCast(_sphereRB.position + _radius * Vector3.up, _radius + 0.25f, -transform.up, out _hit, _maxDistance, _drivableSurface);
// }
// else _grounded = false;
// }
// private void Movement()
// {
// _carVelocity = _carBody.transform.InverseTransformDirection(_carBody.velocity);
// // 🎯 Dynamic friction drift logic
// if (_angleToMove > _brakeAngle && _carVelocity.z > 15f)
// {
// _frictionMaterial.dynamicFriction = 0.01f; // low grip drift
// }
// else
// {
// _frictionMaterial.dynamicFriction = _frictionCurve.Evaluate(Mathf.Abs(_carVelocity.x / 100));
// }
// _turnMultiplyer = _turnCurve.Evaluate(_carVelocity.magnitude / _maxSpeed);
// if (IsGrounded)
// {
// _sign = Mathf.Sign(_carVelocity.z);
// if (Mathf.Abs(_speed) > 0.1f)
// _carBody.AddTorque(Vector3.up * (_turnAI * _sign * _steering * 100f * _turnMultiplyer * steeringFactor));
// // ✨ No braking-based constraint
// if (Mathf.Abs(_speed) > 0.1f)
// _sphereRB.velocity = Vector3.Lerp(_sphereRB.velocity, _carBody.transform.forward * (_speed * _maxSpeed), _accelaration / 10f * Time.fixedDeltaTime);
// _sphereRB.AddForce(-transform.up * (_downforce * _sphereRB.mass));
// _carBody.MoveRotation(Quaternion.Slerp(_carBody.rotation, Quaternion.FromToRotation(_carBody.transform.up, _hit.normal) * _carBody.transform.rotation, 0.12f));
// }
// else
// {
// if (_airControl)
// _carBody.AddTorque(Vector3.up * (_turnAI * _steering * 100f * _turnMultiplyer * steeringFactor));
// _sphereRB.velocity = Vector3.Lerp(_sphereRB.velocity, (_carBody.transform.forward * (_speed * _maxSpeed)) + Vector3.down * (_gravity * 9.8f), (_accelaration / 25f) * Time.deltaTime);
// }
// }
// internal void Sleep()
// {
// this.enabled = false;
// _sphereRB.gameObject.SetActive(false);
// _sphereRB.velocity = Vector3.zero;
// _sphereRB.Sleep();
// _carBody.velocity = Vector3.zero;
// _carBody.Sleep();
// }
// public void UseNOS()
// {
// UseNOS(true);
// nosController._useNOS = true;
// }
// public void UseNOS(bool use, float acceleration = 10f)
// {
// _useNos = use;
// _accelaration = use ? _accelaration + acceleration : _normalAccelaration;
// Debug.Log("accelaration: " + _accelaration);
// }
// internal void WakeUp()
// {
// this.enabled = true;
// _sphereRB.gameObject.SetActive(true);
// }
//}
using UnityEngine;
using CnControls;
public enum SurfaceDetection
{
RayCast,
SphereCast
}
public class VehicleController : MonoBehaviour
{
[SerializeField] float currentSpeed = 0f;
[SerializeField] float steeringAI = 0f;
[SerializeField] bool isAIControlled = false;
[SerializeField] SurfaceDetection groundDetection = SurfaceDetection.RayCast;
[SerializeField] LayerMask driveableSurface = -1;
[SerializeField] float maxSpeed = 100f;
[SerializeField] float baseAcceleration = 10f;
[SerializeField] float steeringSensitivity = 10f;
[SerializeField] float gravityForce = 9.8f;
[SerializeField] float downforce = 5f;
[SerializeField] float brakeThreshold = 30f;
[SerializeField] float targetStoppingDistance = 5f;
[SerializeField] bool allowAirControl = false;
[SerializeField] AnimationCurve _frictionCurve = new AnimationCurve();
[SerializeField] AnimationCurve _turnCurve = new AnimationCurve();
[SerializeField, Range(0, 10)] float bodyTiltAmount = 0.5f;
[SerializeField] Transform carBodyVisual = null;
[SerializeField, Range(0.1f, 1f)] float skidMarkWidth = 0.25f;
[SerializeField, Range(0.1f, 10f)] float skidMarkDuration = 5f;
[SerializeField] Transform wheelFrontLeft = null;
[SerializeField] Transform wheelFrontRight = null;
[SerializeField] Transform wheelRearLeft = null;
[SerializeField] Transform wheelRearRight = null;
[SerializeField] Transform frontLeftAxle = null;
[SerializeField] Transform frontRightAxle = null;
[SerializeField] GameObject speedVisualEffects = null;
[SerializeField] Rigidbody sphereRigidbody = null;
[SerializeField] PhysicMaterial wheelFrictionMaterial = null;
[SerializeField] Transform cameraFollowTarget = null;
private bool isRunning = false;
private bool isOnGround = false;
private bool hasTurboBoost = true;
public bool nosActive = false;
private float accelerationInput = 0f;
private float brakeInput = 0f;
private float accelerationForce = 0f;
private float steeringSign = 0f;
private float inputHorizontal = 0f;
private float inputVertical = 0f;
private float wheelRadius = 0f;
private float desiredTurnAngle = 0f;
private float distanceToTarget = 0f;
private float angleDifference = 0f;
private float maxGroundDistance = 0f;
private float counterSteerValue = 0f;
private float steeringMultiplier = 0f;
private Vector3 velocityLocal = Vector3.zero;
private Vector3 forwardDirection = Vector3.zero;
private Vector3 directionToTarget = Vector3.zero;
private Vector3 movementDirection = Vector3.zero;
private RaycastHit surfaceHit;
private Rigidbody vehicleRigidbody = null;
private VehicleTracker targetTracker = null;
public bool AIControlled => isAIControlled;
public bool Running => isRunning;
public bool Grounded => isOnGround;
public float SkidWidth => skidMarkWidth;
public float SkidDuration => skidMarkDuration;
public float CurrentSpeed => velocityLocal.z;
public float MaximumSpeed => maxSpeed;
public bool InputsActive => inputHorizontal != 0 || inputVertical != 0 || nosActive;
public NOSController nosController;
internal float SteeringSensitivity
{
get => steeringSensitivity;
set => steeringSensitivity = value;
}
public Vector3 LocalVelocity => velocityLocal;
public Transform FollowTarget => cameraFollowTarget;
[Range(0.3f, 1f)] public float steeringFactor = 1f;
private void Awake()
{
vehicleRigidbody = GetComponent<Rigidbody>();
targetTracker = GetComponent<VehicleTracker>();
wheelRadius = sphereRigidbody.GetComponent<SphereCollider>().radius;
nosController = GetComponent<NOSController>();
maxGroundDistance = wheelRadius + 0.5f;
InitializeVehicle();
}
public void InitializeVehicle()
{
isRunning = true;
if (!isAIControlled)
accelerationForce = baseAcceleration;
}
public void StopVehicle()
{
isRunning = false;
velocityLocal = Vector3.zero;
currentSpeed = 0f;
steeringAI = 0f;
}
private void Update()
{
if (!isRunning) return;
UpdateVisuals();
ProcessInputs();
ApplyControls();
}
private void ProcessInputs()
{
inputVertical = CnInputManager.GetAxis("Vertical");
inputHorizontal = CnInputManager.GetAxis("Horizontal");
}
private void UpdateVisuals()
{
counterSteerValue = (Mathf.Abs(velocityLocal.x) > 20f) ? -1f : 1f;
if (wheelFrontLeft) wheelFrontLeft.localRotation = Quaternion.Slerp(wheelFrontLeft.localRotation, Quaternion.Euler(0, 45f * counterSteerValue * steeringAI, 0), 0.1f);
if (wheelFrontRight) wheelFrontRight.localRotation = Quaternion.Slerp(wheelFrontRight.localRotation, Quaternion.Euler(0, 45f * counterSteerValue * steeringAI, 0), 0.1f);
frontLeftAxle?.Rotate(Vector3.right * (CurrentSpeed * 0.75f));
frontRightAxle?.Rotate(Vector3.right * (CurrentSpeed * 0.75f));
wheelRearLeft?.Rotate(Vector3.right * (CurrentSpeed * 0.75f));
wheelRearRight?.Rotate(Vector3.right * (CurrentSpeed * 0.75f));
if (carBodyVisual)
{
if (velocityLocal.z > 1)
carBodyVisual.localRotation = Quaternion.Slerp(carBodyVisual.localRotation,
Quaternion.Euler(Mathf.Lerp(0, -5, velocityLocal.z / maxSpeed), 0, Mathf.Clamp(desiredTurnAngle * steeringAI, -bodyTiltAmount, bodyTiltAmount)), 0.05f);
else
carBodyVisual.localRotation = Quaternion.Slerp(carBodyVisual.localRotation, Quaternion.identity, 0.05f);
}
if (speedVisualEffects)
speedVisualEffects.SetActive(velocityLocal.z > 10);
}
private void ApplyControls()
{
Vector3 targetPoint = targetTracker.TargetPos;
targetPoint.y = transform.position.y;
directionToTarget = (targetPoint - transform.position).normalized;
forwardDirection = transform.forward.normalized;
desiredTurnAngle = Mathf.Abs(Vector3.Angle(forwardDirection, Vector3.ProjectOnPlane(directionToTarget, transform.up)));
distanceToTarget = Vector3.Distance(transform.position, targetTracker.TargetPos);
movementDirection = (targetTracker.TargetPos - transform.position).normalized;
float alignment = Vector3.Dot(transform.forward, movementDirection);
angleDifference = Vector3.Angle(transform.forward, movementDirection);
brakeInput = 0f;
if (distanceToTarget > targetStoppingDistance)
{
accelerationInput = alignment > 0f ? inputVertical : (distanceToTarget > 5f ? inputVertical : -1f);
steeringAI = (Vector3.SignedAngle(transform.forward, movementDirection, Vector3.up) > 0f ? 1f : -1f)
* _turnCurve.Evaluate(desiredTurnAngle / 160f);
}
else
{
steeringAI = 0f;
}
if (!isAIControlled)
steeringAI += inputHorizontal;
currentSpeed = Mathf.Round(CurrentSpeed);
}
private void FixedUpdate()
{
if (!isRunning) return;
CheckGround();
HandleMovement();
}
private void CheckGround()
{
if (groundDetection == SurfaceDetection.RayCast)
{
isOnGround = Physics.Raycast(sphereRigidbody.position, Vector3.down, out surfaceHit, maxGroundDistance, driveableSurface)
|| Physics.Raycast(sphereRigidbody.position + Vector3.forward * 3f, Vector3.down, out surfaceHit, maxGroundDistance, driveableSurface)
|| Physics.Raycast(sphereRigidbody.position - Vector3.forward * 3f, Vector3.down, out surfaceHit, maxGroundDistance, driveableSurface);
}
else if (groundDetection == SurfaceDetection.SphereCast)
{
isOnGround = Physics.SphereCast(sphereRigidbody.position + wheelRadius * Vector3.up, wheelRadius + 0.25f, -transform.up, out surfaceHit, maxGroundDistance, driveableSurface);
}
else isOnGround = false;
}
private void HandleMovement()
{
velocityLocal = vehicleRigidbody.transform.InverseTransformDirection(vehicleRigidbody.velocity);
if (angleDifference > brakeThreshold && velocityLocal.z > 15f)
{
wheelFrictionMaterial.dynamicFriction = 0.01f;
}
else
{
wheelFrictionMaterial.dynamicFriction = _frictionCurve.Evaluate(Mathf.Abs(velocityLocal.x / 100));
}
steeringMultiplier = _turnCurve.Evaluate(velocityLocal.magnitude / maxSpeed);
if (Grounded)
{
steeringSign = Mathf.Sign(velocityLocal.z);
if (Mathf.Abs(accelerationInput) > 0.1f)
vehicleRigidbody.AddTorque(Vector3.up * (steeringAI * steeringSign * steeringSensitivity * 100f * steeringMultiplier * steeringFactor));
if (Mathf.Abs(accelerationInput) > 0.1f)
sphereRigidbody.velocity = Vector3.Lerp(sphereRigidbody.velocity, vehicleRigidbody.transform.forward * (accelerationInput * maxSpeed), accelerationForce / 10f * Time.fixedDeltaTime);
sphereRigidbody.AddForce(-transform.up * (downforce * sphereRigidbody.mass));
vehicleRigidbody.MoveRotation(Quaternion.Slerp(vehicleRigidbody.rotation, Quaternion.FromToRotation(vehicleRigidbody.transform.up, surfaceHit.normal) * vehicleRigidbody.transform.rotation, 0.12f));
}
else
{
if (allowAirControl)
vehicleRigidbody.AddTorque(Vector3.up * (steeringAI * steeringSensitivity * 100f * steeringMultiplier * steeringFactor));
sphereRigidbody.velocity = Vector3.Lerp(sphereRigidbody.velocity, (vehicleRigidbody.transform.forward * (accelerationInput * maxSpeed)) + Vector3.down * (gravityForce * 9.8f), (accelerationForce / 25f) * Time.deltaTime);
}
}
internal void DisableVehicle()
{
enabled = false;
sphereRigidbody.gameObject.SetActive(false);
sphereRigidbody.velocity = Vector3.zero;
sphereRigidbody.Sleep();
vehicleRigidbody.velocity = Vector3.zero;
vehicleRigidbody.Sleep();
}
public void ActivateNOS()
{
ActivateNOS(true);
nosController._isBoosting = true;
}
public void ActivateNOS(bool active, float bonusAcceleration = 10f)
{
nosActive = active;
accelerationForce = active ? accelerationForce + bonusAcceleration : baseAcceleration;
Debug.Log("Acceleration updated: " + accelerationForce);
}
internal void EnableVehicle()
{
enabled = true;
sphereRigidbody.gameObject.SetActive(true);
}
}