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; // 牵引时沿边缘移动速度 private List edgePoints; // 边缘路径点 private int currentEdgeIndex = 0; // 当前吸附边缘点索引 private enum State { Follow, Traction } private State currentState = State.Follow; private Camera mainCamera; void Start() { mainCamera = Camera.main; LoadEdgePointsFromSprite(); } void Update() { Vector2 mouseWorldPos = mainCamera.ScreenToWorldPoint(Input.mousePosition); switch (currentState) { case State.Follow: UpdateFollowState(mouseWorldPos); break; case State.Traction: UpdateTractionState(mouseWorldPos); break; } } // 从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; } // 转换为世界坐标 edgePoints = shapePoints .Select(p => (Vector2)targetSpriteRenderer.transform.TransformPoint(p)) .ToList(); // 假设形状是闭合的,确保首尾连通 if (edgePoints[0] != edgePoints[edgePoints.Count - 1]) { edgePoints.Add(edgePoints[0]); } } // 跟随模式逻辑 private void UpdateFollowState(Vector2 mousePos) { transform.position = mousePos; // 检测距离最近边缘点是否在snapDistance内,进入牵引 int nearestIndex = FindNearestEdgePointIndex(mousePos, out float dist); if (dist < snapDistance) { currentEdgeIndex = nearestIndex; currentState = State.Traction; // 吸附齿轮到边缘点位置 transform.position = edgePoints[currentEdgeIndex]; } } // 牵引模式逻辑 private void UpdateTractionState(Vector2 mousePos) { Vector2 gearPos = (Vector2)transform.position; // 计算鼠标和齿轮距离,超过detachDistance则脱离牵引 if (Vector2.Distance(mousePos, gearPos) > detachDistance) { currentState = State.Follow; return; } // 判断是否按住左键 if (!Input.GetMouseButton(0)) { // 未按键,齿轮固定在边缘点 transform.position = edgePoints[currentEdgeIndex]; } else { // 按住左键,齿轮沿边缘点链移动 // 找到当前边缘点前后点索引,注意闭环处理 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; // 预测如果沿前后方向移动一个小步长后到鼠标的距离 float stepDistance = tractionSpeed * Time.deltaTime; Vector2 posIfMovePrev = currPoint + dirToPrev * stepDistance; Vector2 posIfMoveNext = currPoint + dirToNext * stepDistance; float distPrev = Vector2.Distance(posIfMovePrev, mousePos); float distNext = Vector2.Distance(posIfMoveNext, mousePos); // 选择距离鼠标更近的方向前进 if (distPrev < distNext) { // 向前移动 currentEdgeIndex = prevIndex; transform.position = prevPoint; } else { // 向后移动 currentEdgeIndex = nextIndex; transform.position = nextPoint; } } } // 找到距离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; } }