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

271 lines
7.7 KiB
C#

using System.Collections.Generic;
using UnityEngine;
public class GearFollower : MonoBehaviour
{
[Header("参数")]
public SpriteRenderer targetSpriteRenderer;
public float snapDistance = 0.3f;
public float detachDistance = 1.0f;
public float tractionSpeed = 2f;
[Header("切割轨迹")]
public LineRenderer cutLineRenderer;
public float cutLineWidth = 0.1f;
public float noiseAmount = 0.05f; // 毛刺偏移幅度
private List<Vector2> edgePoints;
private int currentEdgeIndex = 0;
private int tractionDirection = 1;
private enum State { Follow, Traction }
private State currentState = State.Follow;
private Camera mainCamera;
private List<Vector3> cutLinePoints = new List<Vector3>();
private bool isCutting = false;
private float directionSwitchCooldown = 0.2f;
private float directionSwitchTimer = 0f;
void Start()
{
mainCamera = Camera.main;
LoadEdgePointsFromSprite();
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
cutLineRenderer.widthCurve = AnimationCurve.Constant(0, 1, cutLineWidth);
cutLineRenderer.material = new Material(Shader.Find("Sprites/Default"));
cutLineRenderer.numCapVertices = 0; // 线头不圆滑,显锐利
}
}
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<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;
}
float desiredSegmentLength = 0.1f;
edgePoints.Clear();
for (int i = 0; i < shapePoints.Count; i++)
{
Vector2 p1 = targetSpriteRenderer.transform.TransformPoint(shapePoints[i]);
Vector2 p2 = targetSpriteRenderer.transform.TransformPoint(shapePoints[(i + 1) % shapePoints.Count]);
float segmentLength = Vector2.Distance(p1, p2);
int steps = Mathf.Max(1, Mathf.CeilToInt(segmentLength / desiredSegmentLength));
for (int s = 0; s < steps; s++)
{
float t = (float)s / steps;
Vector2 interpolated = Vector2.Lerp(p1, p2, t);
edgePoints.Add(interpolated);
}
}
if ((edgePoints[0] - edgePoints[edgePoints.Count - 1]).sqrMagnitude > 0.0001f)
{
edgePoints.Add(edgePoints[0]);
}
Debug.Log($"边缘点数量:{edgePoints.Count}");
}
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 void UpdateTractionState(Vector2 mousePos)
{
Vector2 gearPos = transform.position;
if (Vector2.Distance(mousePos, gearPos) > detachDistance)
{
currentState = State.Follow;
EndCutting();
return;
}
if (!Input.GetMouseButton(0))
{
EndCutting();
return;
}
if (!isCutting)
{
StartCutting();
}
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);
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);
}
// 记录切割点(带噪声模拟毛糙)
Vector3 noisyPoint = transform.position;
noisyPoint.x += Random.Range(-noiseAmount, noiseAmount);
noisyPoint.y += Random.Range(-noiseAmount, noiseAmount);
AddCutPoint(noisyPoint);
}
private void StartCutting()
{
isCutting = true;
//cutLinePoints.Clear();
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
}
}
private void AddCutPoint(Vector3 point)
{
if (cutLinePoints.Count == 0 || Vector3.Distance(cutLinePoints[cutLinePoints.Count - 1], point) > 0.01f)
{
cutLinePoints.Add(point);
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = cutLinePoints.Count;
cutLineRenderer.SetPositions(cutLinePoints.ToArray());
}
}
}
private void EndCutting()
{
isCutting = false;
// 这里可以添加切割结束后的淡出效果等
}
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;
}
}