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 int tractionDirection = 1; 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); if (Input.GetMouseButton(0)) { transform.Rotate(Vector3.forward, -360f * Time.deltaTime); } switch (currentState) { case State.Follow: UpdateFollowState(mouseWorldPos); break; case State.Traction: UpdateTractionState(mouseWorldPos); break; } } 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; int nearestIndex = FindNearestEdgePointIndex(mousePos, out float dist); if (dist < snapDistance) { currentEdgeIndex = nearestIndex; // 判断应该向前还是向后吸附 Vector2 prev = edgePoints[WrapIndex(currentEdgeIndex - 1)]; Vector2 next = edgePoints[WrapIndex(currentEdgeIndex + 1)]; float distToPrev = Vector2.Distance(mousePos, prev); float distToNext = Vector2.Distance(mousePos, next); tractionDirection = (distToNext < distToPrev) ? 1 : -1; transform.position = edgePoints[currentEdgeIndex]; currentState = State.Traction; } } private float directionSwitchCooldown = 0.2f; private float directionSwitchTimer = 0f; private void UpdateTractionState(Vector2 mousePos) { Vector2 gearPos = transform.position; if (Vector2.Distance(mousePos, gearPos) > detachDistance) { currentState = State.Follow; return; } if (!Input.GetMouseButton(0)) { // 不按左键不移动 return; } int nextIndex = WrapIndex(currentEdgeIndex + tractionDirection); Vector2 from = edgePoints[currentEdgeIndex]; Vector2 to = edgePoints[nextIndex]; // 方向判断优化:使用角度判断是否反方向 Vector2 forward = (to - from).normalized; Vector2 toMouse = (mousePos - from).normalized; float angle = Vector2.SignedAngle(forward, toMouse); // -180 ~ 180 // 当角度大于120度,说明鼠标方向明显是反的 if (Mathf.Abs(angle) > 120f) { directionSwitchTimer += Time.deltaTime; if (directionSwitchTimer > directionSwitchCooldown) { tractionDirection *= -1; directionSwitchTimer = 0f; // 更新前进段 nextIndex = WrapIndex(currentEdgeIndex + tractionDirection); from = edgePoints[currentEdgeIndex]; to = edgePoints[nextIndex]; forward = (to - from).normalized; } } else { directionSwitchTimer = 0f; } Vector2 targetPoint = GetClosestPointOnSegment(from, to, mousePos); Vector2 moveDir = (targetPoint - gearPos); float distance = moveDir.magnitude; float maxStep = tractionSpeed * Time.deltaTime; if (distance <= maxStep) { transform.position = targetPoint; // 如果到达段末,前进一段 if (Vector2.Distance(targetPoint, to) < 0.01f) { currentEdgeIndex = nextIndex; } } else { transform.position += (Vector3)(moveDir.normalized * maxStep); } } 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; } private Vector2 GetClosestPointOnSegment(Vector2 a, Vector2 b, Vector2 p) { Vector2 ab = b - a; float t = Vector2.Dot(p - a, ab) / ab.sqrMagnitude; t = Mathf.Clamp01(t); return a + ab * t; } private int WrapIndex(int index) { if (index < 0) return edgePoints.Count - 2; if (index >= edgePoints.Count - 1) return 0; return index; } }