Race_fields/Assets/_Project/Scripts/Harvesters/AI/HarvesterChaseController.cs

226 lines
7.8 KiB
C#

using RaceFields.Vehicles;
using UnityEngine;
namespace RaceFields.Harvesters.AI
{
[RequireComponent(typeof(Rigidbody))]
public sealed class HarvesterChaseController : MonoBehaviour
{
[Header("Target")]
[SerializeField] private Transform target;
[SerializeField] private bool autoFindPlayer = true;
[SerializeField] private float targetSearchInterval = 0.35f;
[Header("Wheel Colliders")]
[SerializeField] private WheelCollider frontLeftCollider;
[SerializeField] private WheelCollider frontRightCollider;
[SerializeField] private WheelCollider rearLeftCollider;
[SerializeField] private WheelCollider rearRightCollider;
[Header("Wheel Visuals")]
[SerializeField] private Transform frontLeftVisual;
[SerializeField] private Transform frontRightVisual;
[SerializeField] private Transform rearLeftVisual;
[SerializeField] private Transform rearRightVisual;
[Header("Movement")]
[SerializeField] private float maxSpeedKmh = 55f;
[SerializeField] private float motorTorque = 30000f;
[SerializeField] private float brakeTorque = 6500f;
[SerializeField] private float maxSteerAngle = 28f;
[SerializeField] private float steeringResponse = 4f;
[SerializeField] private float stoppingDistance = 1.4f;
[SerializeField] private float reverseDistance = 0.7f;
[SerializeField] private float downforce = 45f;
[Header("Stability")]
[SerializeField] private Vector3 centerOfMass = new Vector3(0f, -0.65f, 0.2f);
private Rigidbody body;
private float nextTargetSearchTime;
private float currentSteerAngle;
private void Awake()
{
body = GetComponent<Rigidbody>();
AutoFindReferences();
body.centerOfMass = centerOfMass;
body.interpolation = RigidbodyInterpolation.Interpolate;
body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
private void FixedUpdate()
{
ResolveTarget();
UpdateDriving();
ApplyDownforce();
}
private void Update()
{
UpdateWheelVisuals();
}
public void SetTarget(Transform newTarget)
{
target = newTarget;
nextTargetSearchTime = Time.time + targetSearchInterval;
}
private void UpdateDriving()
{
if (target == null)
{
ApplyBrake(brakeTorque);
ApplyMotor(0f);
SetSteering(0f);
return;
}
Vector3 localTarget = transform.InverseTransformPoint(target.position);
localTarget.y = 0f;
float distance = localTarget.magnitude;
if (distance < 0.1f)
{
ApplyBrake(brakeTorque);
ApplyMotor(0f);
return;
}
float speedKmh = body.linearVelocity.magnitude * 3.6f;
float targetAngle = Mathf.Atan2(localTarget.x, localTarget.z) * Mathf.Rad2Deg;
float desiredSteer = Mathf.Clamp(targetAngle, -maxSteerAngle, maxSteerAngle);
if (localTarget.z < 0f && Mathf.Abs(localTarget.x) < 0.5f)
desiredSteer = currentSteerAngle >= 0f ? maxSteerAngle : -maxSteerAngle;
SetSteering(desiredSteer);
if (distance <= reverseDistance)
{
ApplyBrake(0f);
ApplyMotor(-motorTorque * 0.45f);
return;
}
if (distance <= stoppingDistance)
{
ApplyBrake(brakeTorque);
ApplyMotor(0f);
return;
}
float angleFactor = Mathf.Clamp(1f - Mathf.Abs(targetAngle) / 130f * 0.5f, 0.65f, 1f);
float speedFactor = speedKmh < maxSpeedKmh ? 1f : 0f;
float torque = motorTorque * angleFactor * speedFactor;
ApplyBrake(0f);
ApplyMotor(torque);
}
private void SetSteering(float desiredAngle)
{
currentSteerAngle = Mathf.Lerp(currentSteerAngle, desiredAngle, steeringResponse * Time.fixedDeltaTime);
if (frontLeftCollider != null)
frontLeftCollider.steerAngle = currentSteerAngle;
if (frontRightCollider != null)
frontRightCollider.steerAngle = currentSteerAngle;
}
private void ApplyMotor(float torque)
{
SetMotorTorque(frontLeftCollider, torque * 0.5f);
SetMotorTorque(frontRightCollider, torque * 0.5f);
SetMotorTorque(rearLeftCollider, torque);
SetMotorTorque(rearRightCollider, torque);
}
private void ApplyBrake(float torque)
{
SetBrakeTorque(frontLeftCollider, torque);
SetBrakeTorque(frontRightCollider, torque);
SetBrakeTorque(rearLeftCollider, torque);
SetBrakeTorque(rearRightCollider, torque);
}
private void ApplyDownforce()
{
body.AddForce(-transform.up * downforce * body.linearVelocity.magnitude, ForceMode.Force);
}
private void UpdateWheelVisuals()
{
SyncWheelVisual(frontLeftCollider, frontLeftVisual);
SyncWheelVisual(frontRightCollider, frontRightVisual);
SyncWheelVisual(rearLeftCollider, rearLeftVisual);
SyncWheelVisual(rearRightCollider, rearRightVisual);
}
private void ResolveTarget()
{
if (target != null || !autoFindPlayer)
return;
if (Time.time < nextTargetSearchTime)
return;
nextTargetSearchTime = Time.time + targetSearchInterval;
ArcadeBuggyController player = Object.FindFirstObjectByType<ArcadeBuggyController>();
if (player != null)
target = player.transform;
}
private void AutoFindReferences()
{
frontLeftCollider ??= FindChildComponent<WheelCollider>("WheelCollider_FL");
frontRightCollider ??= FindChildComponent<WheelCollider>("WheelCollider_FR");
rearLeftCollider ??= FindChildComponent<WheelCollider>("WheelCollider_RL");
rearRightCollider ??= FindChildComponent<WheelCollider>("WheelCollider_RR");
frontLeftVisual ??= FindChildTransform("H_Wheel_FL");
frontRightVisual ??= FindChildTransform("H_Wheel_FR");
rearLeftVisual ??= FindChildTransform("H_Wheel_RL");
rearRightVisual ??= FindChildTransform("H_Wheel_RR");
}
private T FindChildComponent<T>(string childName) where T : Component
{
Transform child = FindChildTransform(childName);
return child != null ? child.GetComponent<T>() : null;
}
private Transform FindChildTransform(string childName)
{
Transform[] children = GetComponentsInChildren<Transform>(true);
for (int i = 0; i < children.Length; i++)
{
if (children[i].name == childName)
return children[i];
}
return null;
}
private static void SetMotorTorque(WheelCollider wheel, float torque)
{
if (wheel != null)
wheel.motorTorque = torque;
}
private static void SetBrakeTorque(WheelCollider wheel, float torque)
{
if (wheel != null)
wheel.brakeTorque = torque;
}
private static void SyncWheelVisual(WheelCollider collider, Transform visual)
{
if (collider == null || visual == null)
return;
collider.GetWorldPose(out Vector3 position, out Quaternion rotation);
visual.SetPositionAndRotation(position, rotation);
}
}
}