Files
aibis-dream/Assets/Scripts/FixSystemNew/BodyModule/GearFollower.cs
T
2025-05-17 01:07:08 +08:00

168 lines
5.2 KiB
C#

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<Vector2> 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<Vector2>();
if (targetSpriteRenderer == null)
{
Debug.LogError("targetSpriteRenderer 为空");
return;
}
List<Vector2> shapePoints = new List<Vector2>();
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;
}
}