2024-11-19 11:48:21 +01:00

242 lines
8.5 KiB
C#

using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Splines;
namespace OmniVehicleAi
{
public class PathProgressTracker : MonoBehaviour
{
public AIVehicleController aiVehicleController;
public SplineContainer splineContainer; // The spline container holding the spline
[Header("Offsets")]
public float offset_A = 15f;
public float offset_AB = 10f;
public float offset_BC = 10f;
public Vector3 progressPoint { get; private set; }
public Vector3 progressTangent { get; private set; }
public Vector3 progressRight { get; private set; }
public float progressDistance { get; private set; } // The progress along the spline.
[HideInInspector] public Vector3 A { get; private set; }
[HideInInspector] public Vector3 B { get; private set; }
[HideInInspector] public Vector3 C { get; private set; }
[HideInInspector] public Vector3 D { get; private set; }
private float speed;
[Header("Lap Settings")]
public int totalLaps = 3; // Total number of laps for the race
public int currentLap = 1; // Current lap count
public bool loopCircuit = true; // If true, the circuit loops for multiple laps
private Spline spline;
private float splineLength;
public UnityEvent OnLapCompleted; // Event for lap completion
public UnityEvent OnAllLapsCompleted; // Event for all laps completion
private void Start()
{
if (aiVehicleController == null)
{
Debug.LogError("AIVehicleController component is missing on this GameObject.");
}
if (splineContainer == null)
{
Debug.LogError("SplineContainer not assigned and none found in the scene.");
enabled = false;
return;
}
spline = splineContainer.Splines[0];
if (spline == null)
{
Debug.LogError("SplineContainer does not contain any splines.");
enabled = false;
return;
}
splineLength = spline.GetLength();
ResetProgress();
}
public void ResetProgress()
{
progressDistance = 0f;
currentLap = 1; // Reset the lap count
}
private void Update()
{
if (aiVehicleController == null || spline == null)
{
return;
}
speed = aiVehicleController.LocalVehiclevelocity.magnitude;
// If loopCircuit is true, the positions can wrap around the spline.
if (loopCircuit)
{
A = GetSplinePoint((progressDistance + offset_A) % splineLength);
B = GetSplinePoint((progressDistance + offset_A + offset_AB) % splineLength);
C = GetSplinePoint((progressDistance + offset_A + offset_AB + offset_BC) % splineLength);
D = GetSplinePoint((progressDistance + offset_A + offset_AB + offset_BC + speed) % splineLength);
}
else
{
// Clamp the positions at the end of the spline when it's not looping.
A = GetSplinePoint(Mathf.Min(progressDistance + offset_A, splineLength));
B = GetSplinePoint(Mathf.Min(progressDistance + offset_A + offset_AB, splineLength));
C = GetSplinePoint(Mathf.Min(progressDistance + offset_A + offset_AB + offset_BC, splineLength));
D = GetSplinePoint(Mathf.Min(progressDistance + offset_A + offset_AB + offset_BC + speed, splineLength));
}
// Get the current progress point and tangent
progressPoint = GetSplinePoint(progressDistance % splineLength);
progressTangent = GetSplineTangent(progressDistance % splineLength);
progressRight = Vector3.Cross(progressTangent, Vector3.up).normalized;
Vector3 progressDelta = progressPoint - transform.position;
// Adjust progress distance based on direction
if (Vector3.Dot(progressDelta, progressTangent) < 0)
{
progressDistance += progressDelta.magnitude * 0.5f;
}
// Check if we've completed a lap
if (progressDistance >= splineLength)
{
CompleteLap();
}
}
private void CompleteLap()
{
if (loopCircuit)
{
progressDistance -= splineLength;// Continue with remaining distance past the spline length
}
currentLap++;
OnLapCompleted?.Invoke();
if(currentLap > totalLaps)
{
OnAllLapsCompleted?.Invoke();
}
// Handle looping logic if enabled
if (loopCircuit && currentLap > totalLaps)
{
currentLap = 1; // Restart from the first lap
}
}
public Vector3 GetClosestPointOnCircuit(Vector3 point)
{
if (spline == null)
{
Debug.LogError("Spline is not initialized.");
return Vector3.zero;
}
float closestDistance = float.MaxValue;
Vector3 closestPoint = Vector3.zero;
for (int i = 0; i < spline.Count - 1; i++)
{
float t0 = (float)i / (spline.Count - 1);
float t1 = (float)(i + 1) / (spline.Count - 1);
int steps = 10;
for (int j = 0; j <= steps; j++)
{
float t = Mathf.Lerp(t0, t1, j / (float)steps);
Vector3 splinePoint = spline.EvaluatePosition(t);
float distance = Vector3.Distance(point, splinePoint);
if (distance < closestDistance)
{
closestDistance = distance;
closestPoint = splinePoint;
}
}
}
return closestPoint;
}
public Vector3 GetSplinePoint(float distance)
{
if (splineContainer == null)
{
Debug.LogError("SplineContainer is not assigned.");
return Vector3.zero;
}
float normalizedDistance = distance / splineLength;
return splineContainer.EvaluatePosition(normalizedDistance);
}
public Vector3 GetSplineTangent(float distance)
{
if (splineContainer == null)
{
Debug.LogError("SplineContainer is not assigned.");
return Vector3.forward;
}
float normalizedDistance = distance / splineLength;
Vector3 splineTangent = splineContainer.EvaluateTangent(normalizedDistance);
return splineTangent.normalized;
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
if(aiVehicleController.AiMode != AIVehicleController.Ai_Mode.PathFollow) { return; }
if (Application.isPlaying)
{
Gizmos.color = Color.cyan;
Gizmos.DrawLine(transform.position, A);
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(progressPoint, 0.2f);
Gizmos.DrawLine(transform.position, progressPoint);
Gizmos.color = new Color(0.8f, 0.1f, 0, 0.8f);
Gizmos.DrawSphere(A, 1);
Gizmos.DrawSphere(B, 1);
Gizmos.DrawSphere(C, 1);
Gizmos.DrawSphere(D, 1);
Gizmos.color = Color.green;
Gizmos.DrawLine(A, B);
Gizmos.DrawLine(B, C);
Gizmos.DrawLine(C, D);
}
else
{
Vector3 tempA = transform.position + transform.forward * offset_A;
Vector3 tempB = transform.position + transform.forward * (offset_A + offset_AB);
Vector3 tempC = transform.position + transform.forward * (offset_A + offset_AB + offset_BC);
Gizmos.color = new Color(0.8f, 0.1f, 0, 0.8f);
Gizmos.DrawSphere(tempA, 1);
Gizmos.DrawSphere(tempB, 1);
Gizmos.DrawSphere(tempC, 1);
}
}
#endif
}
}