466 lines
16 KiB
C#
466 lines
16 KiB
C#
|
|
using System.Collections;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.UI;
|
|||
|
|
using UnityEngine.Events;
|
|||
|
|
[System.Serializable]
|
|||
|
|
public class TrajectoryPoint
|
|||
|
|
{
|
|||
|
|
public int index;
|
|||
|
|
public RectTransform pointTransform;
|
|||
|
|
public Button pointButton;
|
|||
|
|
public Vector2 initialPosition; // 初始位置
|
|||
|
|
public Vector2 currentPosition; // 当前位置
|
|||
|
|
|
|||
|
|
public TrajectoryPoint(int idx, RectTransform transform, Button button, Vector2 initPos)
|
|||
|
|
{
|
|||
|
|
index = idx;
|
|||
|
|
pointTransform = transform;
|
|||
|
|
pointButton = button;
|
|||
|
|
initialPosition = initPos;
|
|||
|
|
currentPosition = initPos;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public class SmoothTrajectorySystem : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
[SerializeField]
|
|||
|
|
RectTransform[] points;
|
|||
|
|
|
|||
|
|
[Header("Movement Settings")]
|
|||
|
|
[SerializeField] float movementDuration = 1f;
|
|||
|
|
[SerializeField] float curveHeight = 50f;
|
|||
|
|
[SerializeField] AnimationCurve movementCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
|||
|
|
|
|||
|
|
[Header("Debug")]
|
|||
|
|
[SerializeField] bool enableDebug = true;
|
|||
|
|
|
|||
|
|
private List<TrajectoryPoint> trajectoryPoints = new List<TrajectoryPoint>();
|
|||
|
|
private bool isMoving = false;
|
|||
|
|
private Vector2 fixedTargetPosition; // 固定的目标位置(0号位置的初始位置)
|
|||
|
|
|
|||
|
|
void Start()
|
|||
|
|
{
|
|||
|
|
InitializePoints();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void InitializePoints()
|
|||
|
|
{
|
|||
|
|
trajectoryPoints.Clear();
|
|||
|
|
|
|||
|
|
if (points == null || points.Length == 0)
|
|||
|
|
{
|
|||
|
|
Debug.LogError("Points array is empty!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置固定的目标位置(0号位置的初始位置)
|
|||
|
|
if (points[0] != null)
|
|||
|
|
{
|
|||
|
|
fixedTargetPosition = points[0].anchoredPosition;
|
|||
|
|
if (enableDebug) Debug.Log($"Fixed target position (index 0 initial): {fixedTargetPosition}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化所有点
|
|||
|
|
for (int i = 0; i < points.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (points[i] == null) continue;
|
|||
|
|
|
|||
|
|
Button button = null;
|
|||
|
|
// 尝试获取按钮(可能在点本身或其子对象上)
|
|||
|
|
if (points[i].GetComponent<Button>() != null)
|
|||
|
|
{
|
|||
|
|
button = points[i].GetComponent<Button>();
|
|||
|
|
}
|
|||
|
|
else if (points[i].childCount > 0 && points[i].GetChild(0).GetComponent<Button>() != null)
|
|||
|
|
{
|
|||
|
|
button = points[i].GetChild(0).GetComponent<Button>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Vector2 initialPosition = points[i].anchoredPosition;
|
|||
|
|
TrajectoryPoint trajectoryPoint = new TrajectoryPoint(i, points[i], button, initialPosition);
|
|||
|
|
trajectoryPoints.Add(trajectoryPoint);
|
|||
|
|
|
|||
|
|
// 设置按钮事件
|
|||
|
|
if (button != null)
|
|||
|
|
{
|
|||
|
|
int index = i;
|
|||
|
|
button.onClick.RemoveAllListeners();
|
|||
|
|
button.onClick.AddListener(() => OnClickItem(index));
|
|||
|
|
|
|||
|
|
if (enableDebug) Debug.Log($"Button event added to point {i}: {points[i].name}, Initial Position: {initialPosition}");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning($"Point {i} ({points[i].name}) has no Button component!");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (enableDebug) Debug.Log($"Initialized {trajectoryPoints.Count} trajectory points with fixed target position");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void OnClickItem(int clickedIndex)
|
|||
|
|
{
|
|||
|
|
if (enableDebug) Debug.Log($"Clicked point index: {clickedIndex}");
|
|||
|
|
|
|||
|
|
if (isMoving)
|
|||
|
|
{
|
|||
|
|
if (enableDebug) Debug.Log("Movement in progress, ignoring click");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查点击的点是否已经在目标位置(基于实际位置,不是索引)
|
|||
|
|
if (IsPointAtTargetPosition(clickedIndex))
|
|||
|
|
{
|
|||
|
|
if (enableDebug) Debug.Log("Clicked point is already at target position, no movement needed");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
StartCoroutine(SequentialMovePointsCoroutine(clickedIndex));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool IsPointAtTargetPosition(int pointIndex)
|
|||
|
|
{
|
|||
|
|
// 检查点是否在固定的目标位置(基于实际坐标位置)
|
|||
|
|
TrajectoryPoint point = trajectoryPoints[pointIndex];
|
|||
|
|
bool isAtTarget = Vector2.Distance(point.currentPosition, fixedTargetPosition) < 0.1f;
|
|||
|
|
|
|||
|
|
if (enableDebug)
|
|||
|
|
{
|
|||
|
|
Debug.Log($"Point {pointIndex} at target position: {isAtTarget} " +
|
|||
|
|
$"(Current: {point.currentPosition}, Target: {fixedTargetPosition}, " +
|
|||
|
|
$"Distance: {Vector2.Distance(point.currentPosition, fixedTargetPosition)})");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return isAtTarget;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator SequentialMovePointsCoroutine(int clickedIndex)
|
|||
|
|
{
|
|||
|
|
isMoving = true;
|
|||
|
|
|
|||
|
|
if (enableDebug) Debug.Log($"Starting sequential movement for point {clickedIndex} to fixed target position");
|
|||
|
|
|
|||
|
|
// 计算移动方向(基于中间点数量)
|
|||
|
|
MovementDirection direction = CalculateMovementDirectionByIntermediatePoints(clickedIndex);
|
|||
|
|
if (enableDebug) Debug.Log($"Movement direction: {direction}");
|
|||
|
|
|
|||
|
|
// 计算每个点的最终目标位置索引
|
|||
|
|
int[] targetIndices = CalculateTargetIndices(clickedIndex, direction);
|
|||
|
|
|
|||
|
|
if (enableDebug)
|
|||
|
|
{
|
|||
|
|
Debug.Log("Target indices:");
|
|||
|
|
for (int i = 0; i < targetIndices.Length; i++)
|
|||
|
|
{
|
|||
|
|
Debug.Log($" Point {i} -> Target index {targetIndices[i]} " +
|
|||
|
|
$"(Position: {trajectoryPoints[targetIndices[i]].initialPosition})");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算每个点的移动路径
|
|||
|
|
List<Vector2>[] movePaths = new List<Vector2>[trajectoryPoints.Count];
|
|||
|
|
for (int i = 0; i < trajectoryPoints.Count; i++)
|
|||
|
|
{
|
|||
|
|
movePaths[i] = CalculateMovePath(i, targetIndices[i]);
|
|||
|
|
if (enableDebug)
|
|||
|
|
{
|
|||
|
|
Debug.Log($"Point {i} path: {movePaths[i].Count} steps " +
|
|||
|
|
$"(Start: {movePaths[i][0]}, End: {movePaths[i][movePaths[i].Count - 1]})");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按步骤顺序移动
|
|||
|
|
int maxPathLength = 0;
|
|||
|
|
for (int i = 0; i < trajectoryPoints.Count; i++)
|
|||
|
|
{
|
|||
|
|
if (movePaths[i].Count > maxPathLength)
|
|||
|
|
maxPathLength = movePaths[i].Count;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行顺序移动
|
|||
|
|
for (int step = 0; step < maxPathLength; step++)
|
|||
|
|
{
|
|||
|
|
if (enableDebug) Debug.Log($"=== Step {step + 1}/{maxPathLength} ===");
|
|||
|
|
|
|||
|
|
bool anyPointMoved = false;
|
|||
|
|
List<Coroutine> moveCoroutines = new List<Coroutine>();
|
|||
|
|
|
|||
|
|
// 移动所有在这一步有目标位置的点
|
|||
|
|
for (int i = 0; i < trajectoryPoints.Count; i++)
|
|||
|
|
{
|
|||
|
|
if (step < movePaths[i].Count - 1) // -1 因为第一个位置是当前位置
|
|||
|
|
{
|
|||
|
|
TrajectoryPoint point = trajectoryPoints[i];
|
|||
|
|
Vector2 startPos = (step == 0) ? point.pointTransform.anchoredPosition : movePaths[i][step];
|
|||
|
|
Vector2 targetPos = movePaths[i][step + 1];
|
|||
|
|
|
|||
|
|
if (Vector2.Distance(startPos, targetPos) > 0.1f)
|
|||
|
|
{
|
|||
|
|
if (enableDebug) Debug.Log($"Moving point {i} from {startPos} to {targetPos}");
|
|||
|
|
Coroutine coroutine = StartCoroutine(MoveSinglePoint(point, startPos, targetPos, i));
|
|||
|
|
moveCoroutines.Add(coroutine);
|
|||
|
|
anyPointMoved = true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (enableDebug) Debug.Log($"Point {i} already at target position, skipping move");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 等待所有移动完成
|
|||
|
|
foreach (Coroutine coroutine in moveCoroutines)
|
|||
|
|
{
|
|||
|
|
yield return coroutine;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (anyPointMoved)
|
|||
|
|
{
|
|||
|
|
// 等待一小段时间让移动更明显
|
|||
|
|
yield return new WaitForSeconds(0.1f);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新所有点的当前位置记录
|
|||
|
|
UpdatePointsCurrentPositions();
|
|||
|
|
|
|||
|
|
if (enableDebug)
|
|||
|
|
{
|
|||
|
|
Debug.Log("Sequential movement completed");
|
|||
|
|
LogCurrentPositions();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
isMoving = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator MoveSinglePoint(TrajectoryPoint point, Vector2 startPos, Vector2 targetPos, int pointIndex)
|
|||
|
|
{
|
|||
|
|
float elapsedTime = 0f;
|
|||
|
|
|
|||
|
|
while (elapsedTime < movementDuration)
|
|||
|
|
{
|
|||
|
|
elapsedTime += Time.deltaTime;
|
|||
|
|
float t = movementCurve.Evaluate(elapsedTime / movementDuration);
|
|||
|
|
|
|||
|
|
Vector2 newPosition = CalculateSmoothPosition(
|
|||
|
|
startPos,
|
|||
|
|
targetPos,
|
|||
|
|
t,
|
|||
|
|
pointIndex,
|
|||
|
|
trajectoryPoints.Count
|
|||
|
|
);
|
|||
|
|
point.pointTransform.anchoredPosition = newPosition;
|
|||
|
|
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保最终位置准确
|
|||
|
|
point.pointTransform.anchoredPosition = targetPos;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MovementDirection CalculateMovementDirectionByIntermediatePoints(int clickedIndex)
|
|||
|
|
{
|
|||
|
|
if (trajectoryPoints.Count <= 2) return MovementDirection.Clockwise;
|
|||
|
|
|
|||
|
|
// 计算顺时针方向的中间点数量(从点击点到0号位置的路径)
|
|||
|
|
int clockwiseIntermediatePoints = clickedIndex - 1;
|
|||
|
|
if (clockwiseIntermediatePoints < 0) clockwiseIntermediatePoints = 0;
|
|||
|
|
|
|||
|
|
// 计算逆时针方向的中间点数量(从点击点到0号位置的路径)
|
|||
|
|
int counterClockwiseIntermediatePoints = trajectoryPoints.Count - clickedIndex - 1;
|
|||
|
|
if (counterClockwiseIntermediatePoints < 0) counterClockwiseIntermediatePoints = 0;
|
|||
|
|
|
|||
|
|
if (enableDebug) Debug.Log($"Clockwise intermediate points: {clockwiseIntermediatePoints}, CounterClockwise: {counterClockwiseIntermediatePoints}");
|
|||
|
|
|
|||
|
|
// 优选顺时针,中间点数量相同时选择顺时针
|
|||
|
|
if (clockwiseIntermediatePoints <= counterClockwiseIntermediatePoints)
|
|||
|
|
return MovementDirection.Clockwise;
|
|||
|
|
else
|
|||
|
|
return MovementDirection.CounterClockwise;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int[] CalculateTargetIndices(int clickedIndex, MovementDirection direction)
|
|||
|
|
{
|
|||
|
|
int[] targetIndices = new int[trajectoryPoints.Count];
|
|||
|
|
int pointCount = trajectoryPoints.Count;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < pointCount; i++)
|
|||
|
|
{
|
|||
|
|
if (direction == MovementDirection.Clockwise)
|
|||
|
|
{
|
|||
|
|
// 顺时针移动:所有点向前移动clickedIndex个位置
|
|||
|
|
// 点击的点会移动到0号位置
|
|||
|
|
targetIndices[i] = (i - clickedIndex + pointCount) % pointCount;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 逆时针移动:所有点向后移动(pointCount - clickedIndex)个位置
|
|||
|
|
// 点击的点会移动到0号位置
|
|||
|
|
targetIndices[i] = (i + (pointCount - clickedIndex)) % pointCount;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return targetIndices;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<Vector2> CalculateMovePath(int currentIndex, int targetIndex)
|
|||
|
|
{
|
|||
|
|
List<Vector2> path = new List<Vector2>();
|
|||
|
|
|
|||
|
|
// 起始位置(当前位置)
|
|||
|
|
path.Add(trajectoryPoints[currentIndex].currentPosition);
|
|||
|
|
|
|||
|
|
// 如果已经在目标位置,直接返回
|
|||
|
|
if (currentIndex == targetIndex &&
|
|||
|
|
Vector2.Distance(trajectoryPoints[currentIndex].currentPosition, trajectoryPoints[targetIndex].initialPosition) < 0.1f)
|
|||
|
|
{
|
|||
|
|
return path;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 目标位置(使用对应索引的初始位置)
|
|||
|
|
Vector2 targetPosition = trajectoryPoints[targetIndex].initialPosition;
|
|||
|
|
path.Add(targetPosition);
|
|||
|
|
|
|||
|
|
return path;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Vector2 CalculateSmoothPosition(Vector2 start, Vector2 end, float t, int pointIndex, int totalPoints)
|
|||
|
|
{
|
|||
|
|
// 基础线性插值
|
|||
|
|
Vector2 basePosition = Vector2.Lerp(start, end, t);
|
|||
|
|
|
|||
|
|
// 添加弧度效果
|
|||
|
|
if (curveHeight > 0)
|
|||
|
|
{
|
|||
|
|
float arcFactor = Mathf.Sin(t * Mathf.PI) * curveHeight;
|
|||
|
|
|
|||
|
|
// 根据点在序列中的位置调整弧线方向
|
|||
|
|
float pointWeight = (float)pointIndex / totalPoints;
|
|||
|
|
Vector2 arcDirection = (end - start).normalized;
|
|||
|
|
|
|||
|
|
// 计算垂直方向(确保有弧度效果)
|
|||
|
|
Vector2 perpendicular = new Vector2(-arcDirection.y, arcDirection.x);
|
|||
|
|
|
|||
|
|
// 应用弧线偏移
|
|||
|
|
Vector2 arcOffset = perpendicular * arcFactor * pointWeight;
|
|||
|
|
|
|||
|
|
return basePosition + arcOffset;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return basePosition;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void UpdatePointsCurrentPositions()
|
|||
|
|
{
|
|||
|
|
foreach (TrajectoryPoint point in trajectoryPoints)
|
|||
|
|
{
|
|||
|
|
point.currentPosition = point.pointTransform.anchoredPosition;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void LogCurrentPositions()
|
|||
|
|
{
|
|||
|
|
Debug.Log("Current positions:");
|
|||
|
|
foreach (TrajectoryPoint point in trajectoryPoints)
|
|||
|
|
{
|
|||
|
|
bool atTarget = Vector2.Distance(point.currentPosition, fixedTargetPosition) < 0.1f;
|
|||
|
|
Debug.Log($" Point {point.index}: {point.currentPosition} (Initial: {point.initialPosition}) [At Target: {atTarget}]");
|
|||
|
|
}
|
|||
|
|
Debug.Log($"Fixed target position: {fixedTargetPosition}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[ContextMenu("Refresh Points")]
|
|||
|
|
void RefreshPoints()
|
|||
|
|
{
|
|||
|
|
// 获取所有直接子对象
|
|||
|
|
List<RectTransform> tempList = new List<RectTransform>();
|
|||
|
|
foreach (Transform child in transform)
|
|||
|
|
{
|
|||
|
|
RectTransform rt = child.GetComponent<RectTransform>();
|
|||
|
|
if (rt != null)
|
|||
|
|
{
|
|||
|
|
tempList.Add(rt);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
points = tempList.ToArray();
|
|||
|
|
InitializePoints();
|
|||
|
|
|
|||
|
|
Debug.Log($"Refreshed points: found {trajectoryPoints.Count} trajectory points");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[ContextMenu("Reset to Initial Positions")]
|
|||
|
|
void ResetToInitialPositions()
|
|||
|
|
{
|
|||
|
|
if (isMoving) return;
|
|||
|
|
|
|||
|
|
foreach (TrajectoryPoint point in trajectoryPoints)
|
|||
|
|
{
|
|||
|
|
if (point.pointTransform != null)
|
|||
|
|
{
|
|||
|
|
point.pointTransform.anchoredPosition = point.initialPosition;
|
|||
|
|
point.currentPosition = point.initialPosition;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Debug.Log("Reset all points to initial positions");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[ContextMenu("Move Point 0 Away From Target")]
|
|||
|
|
void MovePoint0AwayFromTarget()
|
|||
|
|
{
|
|||
|
|
if (trajectoryPoints.Count > 0 && !isMoving)
|
|||
|
|
{
|
|||
|
|
// 将0号点移动到远离目标位置的位置,用于测试
|
|||
|
|
Vector2 newPosition = trajectoryPoints[0].currentPosition + new Vector2(100, 100);
|
|||
|
|
trajectoryPoints[0].pointTransform.anchoredPosition = newPosition;
|
|||
|
|
trajectoryPoints[0].currentPosition = newPosition;
|
|||
|
|
Debug.Log($"Moved point 0 away from target: {newPosition}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[ContextMenu("Test Movement - Point 0")]
|
|||
|
|
void TestMovement0()
|
|||
|
|
{
|
|||
|
|
if (!isMoving && trajectoryPoints.Count > 0)
|
|||
|
|
{
|
|||
|
|
OnClickItem(0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[ContextMenu("Test Movement - Point 1")]
|
|||
|
|
void TestMovement1()
|
|||
|
|
{
|
|||
|
|
if (!isMoving && trajectoryPoints.Count > 1)
|
|||
|
|
{
|
|||
|
|
OnClickItem(1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[ContextMenu("Test Movement - Last Point")]
|
|||
|
|
void TestMovementLast()
|
|||
|
|
{
|
|||
|
|
if (!isMoving && trajectoryPoints.Count > 1)
|
|||
|
|
{
|
|||
|
|
OnClickItem(trajectoryPoints.Count - 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[ContextMenu("Check All Points Target Status")]
|
|||
|
|
void CheckAllPointsTargetStatus()
|
|||
|
|
{
|
|||
|
|
Debug.Log("=== All Points Target Status ===");
|
|||
|
|
for (int i = 0; i < trajectoryPoints.Count; i++)
|
|||
|
|
{
|
|||
|
|
bool atTarget = IsPointAtTargetPosition(i);
|
|||
|
|
Debug.Log($"Point {i}: At Target = {atTarget}, Position = {trajectoryPoints[i].currentPosition}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
enum MovementDirection
|
|||
|
|
{
|
|||
|
|
Clockwise,
|
|||
|
|
CounterClockwise
|
|||
|
|
}
|
|||
|
|
}
|