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

323 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)
{
// 当前段的两个点
Vector2 a = edgePoints[currentEdgeIndex];
Vector2 b = edgePoints[currentEdgeIndex + 1];
// 投影鼠标位置到当前段
Vector2 projected = ProjectPointOnSegment(mousePos, a, b);
// 鼠标离齿轮足够远才移动(模拟牵引力)
float tractionThreshold = 0.1f;
float distToMouse = Vector2.Distance(transform.position, mousePos);
if (distToMouse < tractionThreshold)
return;
// 移动齿轮朝向投影点
transform.position = Vector2.MoveTowards(transform.position, projected, tractionSpeed * Time.deltaTime);
// 标记切割
if (!cutSegments[currentEdgeIndex])
{
cutSegments[currentEdgeIndex] = true;
Debug.Log($"切割线段 {currentEdgeIndex} ({a}->{b})");
}
// 判断是否前进或后退段
float switchThreshold = 0.05f;
// 往前走(靠近 b 点且还有下一段)
if (Vector2.Distance(projected, b) < switchThreshold && currentEdgeIndex < edgePoints.Count - 2)
{
currentEdgeIndex++;
}
// 往后退(靠近 a 点且还有上一段)
else if (Vector2.Distance(projected, a) < switchThreshold && currentEdgeIndex > 0)
{
currentEdgeIndex--;
}
}
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;
}
}
}