TCI/Assets/Script/SplineMovement.cs
2025-11-17 15:36:52 +07:00

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;
}