using System.Collections.Generic; using System.Linq; using UnityEngine; public class GearFollower : MonoBehaviour { [Header("参数")] public SpriteRenderer targetSpriteRenderer; // 目标图片 public float snapDistance = 0.3f; // 距离边缘多少时吸附 public float detachDistance = 1.0f; // 超过多少距离脱离牵引 public float tractionSpeed = 2f; // 牵引时沿边缘移动速度 public float rotationSpeed = 360f; // 齿轮旋转速度(度/秒) public float smoothTime = 0.1f; // 平滑过渡时间 public float edgeSmoothTime = 0.05f; // 边缘移动平滑时间 public float edgeScaleFactor = 1.1f; // 边缘点采样放大倍数 public Color normalColor = Color.white; // 正常状态颜色 public Color tractionColor = Color.yellow; // 牵引状态颜色 private List edgePoints; // 边缘路径点 private int currentEdgeIndex = 0; // 当前吸附边缘点索引 private SpriteRenderer gearSpriteRenderer; // 齿轮的SpriteRenderer private Vector2 currentVelocity; // 用于平滑移动 private float currentRotationVelocity; // 用于平滑旋转 private enum State { Follow, Traction } private State currentState = State.Follow; private Camera mainCamera; private Vector2 targetEdgePosition; // 目标边缘位置 private float currentEdgeProgress; // 当前边缘移动进度 void Start() { mainCamera = Camera.main; gearSpriteRenderer = GetComponent(); if (gearSpriteRenderer == null) { Debug.LogError("齿轮对象缺少SpriteRenderer组件"); } LoadEdgePointsFromSprite(); } void Update() { Vector2 mouseWorldPos = mainCamera.ScreenToWorldPoint(Input.mousePosition); switch (currentState) { case State.Follow: UpdateFollowState(mouseWorldPos); break; case State.Traction: UpdateTractionState(mouseWorldPos); break; } // 更新齿轮旋转 UpdateGearRotation(); // 更新视觉反馈 UpdateVisualFeedback(); } // 更新齿轮旋转 private void UpdateGearRotation() { if (currentState == State.Traction && Input.GetMouseButton(0)) { // 在牵引状态下按住左键时旋转 transform.Rotate(0, 0, rotationSpeed * Time.deltaTime); } } // 更新视觉反馈 private void UpdateVisualFeedback() { if (gearSpriteRenderer != null) { gearSpriteRenderer.color = currentState == State.Traction ? tractionColor : normalColor; } } // 从Sprite的PhysicsShape获取边缘点,构成闭环路径 private void LoadEdgePointsFromSprite() { edgePoints = new List(); if (targetSpriteRenderer == null) { Debug.LogError("targetSpriteRenderer 为空"); return; } List shapePoints = new List(); targetSpriteRenderer.sprite.GetPhysicsShape(0, shapePoints); if (shapePoints.Count == 0) { Debug.LogError("目标Sprite没有物理形状点"); return; } // 计算形状的中心点 Vector2 center = Vector2.zero; foreach (Vector2 point in shapePoints) { center += point; } center /= shapePoints.Count; // 转换为世界坐标并应用放大 edgePoints = shapePoints .Select(p => { // 计算从中心点到当前点的向量 Vector2 dirFromCenter = p - center; // 应用放大 Vector2 scaledPoint = center + dirFromCenter * edgeScaleFactor; // 转换为世界坐标 return (Vector2)targetSpriteRenderer.transform.TransformPoint(scaledPoint); }) .ToList(); // 假设形状是闭合的,确保首尾连通 if (edgePoints[0] != edgePoints[edgePoints.Count - 1]) { edgePoints.Add(edgePoints[0]); } } // 跟随模式逻辑 private void UpdateFollowState(Vector2 mousePos) { // 使用平滑移动 transform.position = Vector2.SmoothDamp(transform.position, mousePos, ref currentVelocity, smoothTime); // 检测距离最近边缘点是否在snapDistance内,进入牵引 int nearestIndex = FindNearestEdgePointIndex(mousePos, out float dist); if (dist < snapDistance) { currentEdgeIndex = nearestIndex; currentState = State.Traction; // 切换到牵引状态时,调整渲染顺序 if (gearSpriteRenderer != null) { gearSpriteRenderer.sortingOrder = 1; } } } // 牵引模式逻辑 private void UpdateTractionState(Vector2 mousePos) { Vector2 gearPos = (Vector2)transform.position; // 计算鼠标和齿轮距离,超过detachDistance则脱离牵引 if (Vector2.Distance(mousePos, gearPos) > detachDistance) { currentState = State.Follow; // 切换回跟随状态时,调整渲染顺序 if (gearSpriteRenderer != null) { gearSpriteRenderer.sortingOrder = 5; } return; } // 判断是否按住左键 if (!Input.GetMouseButton(0)) { // 未按键,齿轮固定在边缘点 transform.position = Vector2.SmoothDamp(transform.position, edgePoints[currentEdgeIndex], ref currentVelocity, smoothTime); } else { // 按住左键,齿轮沿边缘点链移动 MoveAlongEdge(mousePos); } } // 沿边缘移动的逻辑 private void MoveAlongEdge(Vector2 mousePos) { // 找到当前边缘点前后点索引,注意闭环处理 int prevIndex = (currentEdgeIndex - 1 + edgePoints.Count) % edgePoints.Count; int nextIndex = (currentEdgeIndex + 1) % edgePoints.Count; Vector2 prevPoint = edgePoints[prevIndex]; Vector2 currPoint = edgePoints[currentEdgeIndex]; Vector2 nextPoint = edgePoints[nextIndex]; // 计算向前和向后移动方向 Vector2 dirToPrev = (prevPoint - currPoint).normalized; Vector2 dirToNext = (nextPoint - currPoint).normalized; // 计算鼠标到当前点的向量 Vector2 mouseToCurrent = mousePos - currPoint; // 计算鼠标在前后方向上的投影 float projToPrev = Vector2.Dot(mouseToCurrent, dirToPrev); float projToNext = Vector2.Dot(mouseToCurrent, dirToNext); // 根据投影决定移动方向 Vector2 targetDir; if (Mathf.Abs(projToPrev) > Mathf.Abs(projToNext)) { targetDir = dirToPrev; currentEdgeIndex = prevIndex; targetEdgePosition = prevPoint; } else { targetDir = dirToNext; currentEdgeIndex = nextIndex; targetEdgePosition = nextPoint; } // 计算目标位置(在边缘上) Vector2 currentPos = (Vector2)transform.position; Vector2 targetPos = Vector2.Lerp(currPoint, targetEdgePosition, currentEdgeProgress); // 使用平滑移动 transform.position = Vector2.SmoothDamp(currentPos, targetPos, ref currentVelocity, edgeSmoothTime); // 更新边缘移动进度 float moveDistance = tractionSpeed * Time.deltaTime; float totalDistance = Vector2.Distance(currPoint, targetEdgePosition); currentEdgeProgress += moveDistance / totalDistance; // 如果到达目标点,重置进度 if (currentEdgeProgress >= 1f) { currentEdgeProgress = 0f; } } // 找到距离pos最近的边缘点索引和距离 private int FindNearestEdgePointIndex(Vector2 pos, out float minDist) { int nearestIndex = 0; minDist = float.MaxValue; for (int i = 0; i < edgePoints.Count; i++) { float dist = Vector2.Distance(pos, edgePoints[i]); if (dist < minDist) { minDist = dist; nearestIndex = i; } } return nearestIndex; } }