Files
aibis-dream/Assets/Scripts/FixSystemNew/BodyModule/GearFollower.cs
T
2025-05-16 22:31:11 +08:00

321 lines
9.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class GearFollowerWithVisualCut : 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 edgeScaleFactor = 1.1f;
public Color normalEdgeColor = Color.white;
public Color cutEdgeColor = Color.yellow;
private List<Vector2> edgePoints;
private int currentEdgeIndex = 0;
private SpriteRenderer gearSpriteRenderer;
private Camera mainCamera;
private enum State { Follow, Attract, Traction }
private State currentState = State.Follow;
private bool[] cutSegments;
private LineRenderer edgeLineRenderer; // 绘制全部边缘
private LineRenderer cutLineRenderer; // 绘制切割线段
void Start()
{
mainCamera = Camera.main;
gearSpriteRenderer = GetComponent<SpriteRenderer>();
LoadEdgePointsFromSprite();
cutSegments = new bool[edgePoints.Count - 1];
// 设置主边缘线条渲染器
edgeLineRenderer = GetComponent<LineRenderer>();
edgeLineRenderer.positionCount = edgePoints.Count;
edgeLineRenderer.loop = false;
edgeLineRenderer.startWidth = 0.05f;
edgeLineRenderer.endWidth = 0.05f;
edgeLineRenderer.material = new Material(Shader.Find("Sprites/Default"));
edgeLineRenderer.startColor = normalEdgeColor;
edgeLineRenderer.endColor = normalEdgeColor;
edgeLineRenderer.sortingOrder = 3;
for (int i = 0; i < edgePoints.Count; i++)
{
edgeLineRenderer.SetPosition(i, edgePoints[i]);
}
// 创建用于切割线段的LineRenderer
GameObject cutLineObj = new GameObject("CutLineRenderer");
cutLineObj.transform.parent = this.transform;
cutLineRenderer = cutLineObj.AddComponent<LineRenderer>();
cutLineRenderer.positionCount = 0;
cutLineRenderer.loop = false;
cutLineRenderer.startWidth = 0.07f;
cutLineRenderer.endWidth = 0.07f;
cutLineRenderer.material = new Material(Shader.Find("Sprites/Default"));
cutLineRenderer.startColor = cutEdgeColor;
cutLineRenderer.endColor = cutEdgeColor;
cutLineRenderer.sortingOrder = 4;
}
void Update()
{
Vector2 mouseWorldPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
switch (currentState)
{
case State.Follow:
UpdateFollowState(mouseWorldPos);
break;
case State.Attract:
UpdateAttractState(mouseWorldPos);
break;
case State.Traction:
UpdateTractionState(mouseWorldPos);
break;
}
UpdateGearRotation();
UpdateVisualFeedback();
UpdateCutLineRenderer();
CheckAllSegmentsCut();
}
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 || currentState == State.Attract) ? cutEdgeColor : normalEdgeColor;
}
}
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;
}
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 = mousePos;
int nearestIndex = FindNearestEdgePointIndex(mousePos, out float dist);
if (dist < snapDistance)
{
currentEdgeIndex = nearestIndex;
currentState = State.Attract;
if (gearSpriteRenderer != null)
gearSpriteRenderer.sortingOrder = 1;
}
}
private void UpdateAttractState(Vector2 mousePos)
{
Vector2 closestPoint = Vector2.zero;
float closestDist = float.MaxValue;
int closestSegmentStartIndex = 0;
for (int i = 0; i < edgePoints.Count - 1; i++)
{
Vector2 a = edgePoints[i];
Vector2 b = edgePoints[i + 1];
Vector2 projected = ProjectPointOnSegment(mousePos, a, b);
float dist = Vector2.Distance(mousePos, projected);
if (dist < closestDist)
{
closestDist = dist;
closestPoint = projected;
closestSegmentStartIndex = i;
}
}
transform.position = closestPoint;
currentEdgeIndex = closestSegmentStartIndex;
if (Vector2.Distance(mousePos, closestPoint) > detachDistance)
{
currentState = State.Follow;
if (gearSpriteRenderer != null)
gearSpriteRenderer.sortingOrder = 5;
return;
}
if (Input.GetMouseButton(0))
{
currentState = State.Traction;
}
}
private void UpdateTractionState(Vector2 mousePos)
{
if (Vector2.Distance(mousePos, transform.position) > detachDistance)
{
currentState = State.Follow;
if (gearSpriteRenderer != null)
gearSpriteRenderer.sortingOrder = 5;
return;
}
if (Input.GetMouseButton(0))
{
MoveAlongEdge(mousePos);
}
}
private void MoveAlongEdge(Vector2 mousePos)
{
float closestDist = float.MaxValue;
Vector2 closestPoint = Vector2.zero;
int closestSegmentStartIndex = 0;
for (int i = 0; i < edgePoints.Count - 1; i++)
{
Vector2 a = edgePoints[i];
Vector2 b = edgePoints[i + 1];
Vector2 projected = ProjectPointOnSegment(mousePos, a, b);
float dist = Vector2.Distance(mousePos, projected);
if (dist < closestDist)
{
closestDist = dist;
closestPoint = projected;
closestSegmentStartIndex = i;
}
}
float stopThreshold = 0.05f;
if (Vector2.Distance(transform.position, closestPoint) < stopThreshold)
{
return;
}
transform.position = Vector2.MoveTowards(transform.position, closestPoint, tractionSpeed * Time.deltaTime);
currentEdgeIndex = closestSegmentStartIndex;
// 标记当前线段已被切割
if (!cutSegments[currentEdgeIndex])
{
cutSegments[currentEdgeIndex] = true;
Debug.Log($"切割了线段 {currentEdgeIndex} ({edgePoints[currentEdgeIndex]}->{edgePoints[currentEdgeIndex + 1]})");
}
}
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 ProjectPointOnSegment(Vector2 point, Vector2 a, Vector2 b)
{
Vector2 ab = b - a;
float abLengthSquared = ab.sqrMagnitude;
if (abLengthSquared == 0f) return a;
float t = Vector2.Dot(point - a, ab) / abLengthSquared;
t = Mathf.Clamp01(t);
return a + t * ab;
}
private void UpdateCutLineRenderer()
{
// 统计所有已切割线段点数 = 2*切割线段数(start和end
int count = cutSegments.Count(cut => cut);
if (count == 0)
{
cutLineRenderer.positionCount = 0;
return;
}
// 每个切割线段2个点
cutLineRenderer.positionCount = count * 2;
int index = 0;
for (int i = 0; i < cutSegments.Length; i++)
{
if (cutSegments[i])
{
cutLineRenderer.SetPosition(index, edgePoints[i]);
cutLineRenderer.SetPosition(index + 1, edgePoints[i + 1]);
index += 2;
}
}
}
private void CheckAllSegmentsCut()
{
if (cutSegments == null) return;
if (System.Array.TrueForAll(cutSegments, cut => cut))
{
Debug.Log("所有边缘线段都已经切割完成!");
enabled = false;
}
}
}