267 lines
8.4 KiB
C#
267 lines
8.4 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Splines;
|
|
using Unity.Mathematics;
|
|
using System.Linq;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
public class MultiSplineFollower : MonoBehaviour
|
|
{
|
|
[Header("Tuyến đường 1")]
|
|
public SplineRoute[] routes;
|
|
[Header("Tuyến đường 2")]
|
|
public SplineRoute[] routes1;
|
|
[Header("Tuyến đường 3")]
|
|
public SplineRoute[] routes2;
|
|
|
|
[Header("Cài đặt chuyển động")]
|
|
public float speed = 2f;
|
|
public float rotationSpeed = 180f;
|
|
public float angleThreshold = 30f;
|
|
|
|
[Header("Cài đặt quét TCIA")]
|
|
public float scanRadius = 0.5f; // bán kính quét đối tượng TCIA quanh follower
|
|
|
|
private int currentRouteIndex = 0;
|
|
private int currentRouteGroup = 0; // 0 = routes, 1 = routes1, 2 = routes2
|
|
private float t = 0f;
|
|
private bool onFirstSpline = true;
|
|
private bool isDelaying = false;
|
|
|
|
void Start()
|
|
{
|
|
if (GetActiveRoutes() == null || GetActiveRoutes().Length == 0) return;
|
|
|
|
var activeRoutes = GetActiveRoutes();
|
|
InitRoute(activeRoutes[0]);
|
|
t = activeRoutes[0].tStart;
|
|
transform.position = activeRoutes[0].splineStart.EvaluatePosition(0, t);
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
var activeRoutes = GetActiveRoutes();
|
|
if (activeRoutes == null || activeRoutes.Length == 0) return;
|
|
if (currentRouteIndex >= activeRoutes.Length) return;
|
|
if (isDelaying) return;
|
|
|
|
SplineRoute route = activeRoutes[currentRouteIndex];
|
|
if (onFirstSpline)
|
|
MoveOnSpline(route.splineStart, ref t, route.tCommonA, true, route);
|
|
else
|
|
MoveOnSpline(route.splineEnd, ref t, route.tEnd, false, route);
|
|
}
|
|
|
|
SplineRoute[] GetActiveRoutes()
|
|
{
|
|
switch (currentRouteGroup)
|
|
{
|
|
case 0: return routes;
|
|
case 1: return routes1;
|
|
case 2: return routes2;
|
|
default: return null;
|
|
}
|
|
}
|
|
|
|
void InitRoute(SplineRoute route)
|
|
{
|
|
BezierKnot knotStart = route.splineStart.Spline.Knots.ElementAt(route.startKnotIndex);
|
|
BezierKnot knotEnd = route.splineEnd.Spline.Knots.ElementAt(route.endKnotIndex);
|
|
|
|
Vector3 knotX = route.splineStart.transform.TransformPoint((Vector3)knotStart.Position);
|
|
Vector3 knotY = route.splineEnd.transform.TransformPoint((Vector3)knotEnd.Position);
|
|
|
|
route.commonPoint = FindCommonPoint(route.splineStart, route.splineEnd);
|
|
|
|
float3 nearest;
|
|
SplineUtility.GetNearestPoint(route.splineStart.Spline,
|
|
(float3)route.splineStart.transform.InverseTransformPoint(knotX),
|
|
out nearest, out route.tStart);
|
|
|
|
SplineUtility.GetNearestPoint(route.splineStart.Spline,
|
|
(float3)route.splineStart.transform.InverseTransformPoint(route.commonPoint),
|
|
out nearest, out route.tCommonA);
|
|
|
|
SplineUtility.GetNearestPoint(route.splineEnd.Spline,
|
|
(float3)route.splineEnd.transform.InverseTransformPoint(route.commonPoint),
|
|
out nearest, out route.tCommonB);
|
|
|
|
SplineUtility.GetNearestPoint(route.splineEnd.Spline,
|
|
(float3)route.splineEnd.transform.InverseTransformPoint(knotY),
|
|
out nearest, out route.tEnd);
|
|
|
|
onFirstSpline = true;
|
|
}
|
|
|
|
void MoveOnSpline(SplineContainer spline, ref float t, float targetT, bool first, SplineRoute route)
|
|
{
|
|
Vector3 pos = spline.EvaluatePosition(0, t);
|
|
Vector3 tangent = Vector3.Normalize((Vector3)spline.EvaluateTangent(0, t));
|
|
if (route.reverseDirection) tangent = -tangent;
|
|
|
|
float angle = Vector3.Angle(transform.forward, tangent);
|
|
|
|
if (angle > angleThreshold)
|
|
{
|
|
Quaternion targetRot = Quaternion.LookRotation(tangent, Vector3.up);
|
|
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRot, rotationSpeed * Time.deltaTime);
|
|
return;
|
|
}
|
|
|
|
float dir = (targetT > t) ? 1f : -1f;
|
|
t += dir * (Time.deltaTime * speed / spline.CalculateLength(0));
|
|
|
|
if ((dir > 0 && t >= targetT) || (dir < 0 && t <= targetT))
|
|
t = targetT;
|
|
|
|
pos = spline.EvaluatePosition(0, t);
|
|
tangent = Vector3.Normalize((Vector3)spline.EvaluateTangent(0, t));
|
|
if (route.reverseDirection) tangent = -tangent;
|
|
|
|
transform.position = pos;
|
|
transform.rotation = Quaternion.LookRotation(tangent, Vector3.up);
|
|
|
|
if (first && Mathf.Approximately(t, targetT))
|
|
{
|
|
onFirstSpline = false;
|
|
t = route.tCommonB;
|
|
}
|
|
else if (!first && Mathf.Approximately(t, targetT))
|
|
{
|
|
if (route.delayAtEnd)
|
|
{
|
|
StartCoroutine(DelayAndNextRoute(route));
|
|
}
|
|
else
|
|
{
|
|
NextRoute();
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerator DelayAndNextRoute(SplineRoute route)
|
|
{
|
|
isDelaying = true;
|
|
|
|
if (route.attachTCIAObjects)
|
|
{
|
|
Debug.Log($"[{route.splineStart.name}] attachTCIAObjects = TRUE → quét và gắn TCIA");
|
|
AttachTCIAChildren();
|
|
}
|
|
else
|
|
{
|
|
DetachTCIAChildren();
|
|
}
|
|
|
|
yield return new WaitForSeconds(2f);
|
|
|
|
isDelaying = false;
|
|
NextRoute();
|
|
}
|
|
|
|
void AttachTCIAChildren()
|
|
{
|
|
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
|
|
foreach (GameObject obj in allObjects)
|
|
{
|
|
if (!obj.activeInHierarchy || obj == this.gameObject)
|
|
continue;
|
|
|
|
if (obj.name.ToUpper().Contains("TCIA"))
|
|
{
|
|
float distance = Vector3.Distance(transform.position, obj.transform.position);
|
|
if (distance <= scanRadius)
|
|
{
|
|
obj.transform.SetParent(this.transform, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DetachTCIAChildren()
|
|
{
|
|
List<Transform> childrenToDetach = new List<Transform>();
|
|
|
|
foreach (Transform child in transform)
|
|
{
|
|
if (child.name.ToUpper().Contains("TCIA"))
|
|
{
|
|
childrenToDetach.Add(child);
|
|
}
|
|
}
|
|
|
|
foreach (Transform child in childrenToDetach)
|
|
{
|
|
child.SetParent(null, true);
|
|
}
|
|
}
|
|
|
|
void NextRoute()
|
|
{
|
|
var activeRoutes = GetActiveRoutes();
|
|
|
|
currentRouteIndex++;
|
|
if (activeRoutes != null && currentRouteIndex < activeRoutes.Length)
|
|
{
|
|
InitRoute(activeRoutes[currentRouteIndex]);
|
|
t = activeRoutes[currentRouteIndex].tStart;
|
|
transform.position = activeRoutes[currentRouteIndex].splineStart.EvaluatePosition(0, t);
|
|
}
|
|
else
|
|
{
|
|
// Hết nhóm hiện tại → chuyển sang nhóm tiếp theo
|
|
currentRouteGroup++;
|
|
currentRouteIndex = 0;
|
|
|
|
var nextGroupRoutes = GetActiveRoutes();
|
|
|
|
if (nextGroupRoutes != null && nextGroupRoutes.Length > 0)
|
|
{
|
|
Debug.Log($"Chuyển sang nhóm tuyến: {currentRouteGroup}");
|
|
InitRoute(nextGroupRoutes[0]);
|
|
t = nextGroupRoutes[0].tStart;
|
|
transform.position = nextGroupRoutes[0].splineStart.EvaluatePosition(0, t);
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("Hoàn thành toàn bộ tuyến đường!");
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 FindCommonPoint(SplineContainer s1, SplineContainer s2)
|
|
{
|
|
foreach (var knotA in s1.Spline.Knots)
|
|
{
|
|
Vector3 pA = s1.transform.TransformPoint((Vector3)knotA.Position);
|
|
foreach (var knotB in s2.Spline.Knots)
|
|
{
|
|
Vector3 pB = s2.transform.TransformPoint((Vector3)knotB.Position);
|
|
if (Vector3.Distance(pA, pB) < 0.01f)
|
|
return pA;
|
|
}
|
|
}
|
|
Debug.LogWarning("Không tìm thấy điểm chung!");
|
|
return Vector3.zero;
|
|
}
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class SplineRoute
|
|
{
|
|
public SplineContainer splineStart;
|
|
public int startKnotIndex;
|
|
public SplineContainer splineEnd;
|
|
public int endKnotIndex;
|
|
public bool reverseDirection;
|
|
public bool delayAtEnd;
|
|
public bool attachTCIAObjects = true; // true = gắn TCIA, false = tách TCIA khi delay
|
|
|
|
[HideInInspector] public Vector3 commonPoint;
|
|
[HideInInspector] public float tStart;
|
|
[HideInInspector] public float tCommonA;
|
|
[HideInInspector] public float tCommonB;
|
|
[HideInInspector] public float tEnd;
|
|
}
|