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