增加卷边

This commit is contained in:
2025-05-17 04:36:56 +08:00
parent 7b8c648f66
commit b8709a86e7
2 changed files with 225 additions and 119 deletions
@@ -9,14 +9,10 @@ public class GearFollower : MonoBehaviour
public float detachDistance = 1.0f;
public float tractionSpeed = 2f;
[Header("切割效果参数")]
public float cutEdgeExpandDuration = 0.5f; // 卷边展开时间
public float cutEdgeCoolDuration = 3f; // 冷却时间
public float cutEdgeMaxOffset = 0.05f; // 卷边最大偏移量
public Gradient cutEdgeColorGradient; // 颜色渐变(从高温到冷却)
public Material cutEdgeMaterial; // 用于绘制卷边的材质
[Header("切割轨迹")]
public LineRenderer cutLineRenderer;
public float cutLineWidth = 0.1f;
public float noiseAmount = 0.05f; // 毛刺偏移幅度
private List<Vector2> edgePoints;
private int currentEdgeIndex = 0;
@@ -27,19 +23,24 @@ public class GearFollower : MonoBehaviour
private Camera mainCamera;
// 记录切割的边段及其动画状态
private class CutSegment
{
public int startIndex;
public float timeElapsed; // 0~卷边展开+冷却总时长
public LineRenderer lineRenderer;
}
private List<CutSegment> cutSegments = new List<CutSegment>();
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()
@@ -60,8 +61,6 @@ public class GearFollower : MonoBehaviour
UpdateTractionState(mouseWorldPos);
break;
}
UpdateCutSegments();
}
private void LoadEdgePointsFromSprite()
@@ -82,7 +81,7 @@ public class GearFollower : MonoBehaviour
return;
}
float desiredSegmentLength = 0.1f; // 你可以根据需要调整这个值
float desiredSegmentLength = 0.1f;
edgePoints.Clear();
@@ -102,7 +101,6 @@ public class GearFollower : MonoBehaviour
}
}
// 确保闭环
if ((edgePoints[0] - edgePoints[edgePoints.Count - 1]).sqrMagnitude > 0.0001f)
{
edgePoints.Add(edgePoints[0]);
@@ -120,7 +118,6 @@ public class GearFollower : MonoBehaviour
{
currentEdgeIndex = nearestIndex;
// 判断应该向前还是向后吸附
Vector2 prev = edgePoints[WrapIndex(currentEdgeIndex - 1)];
Vector2 next = edgePoints[WrapIndex(currentEdgeIndex + 1)];
@@ -134,9 +131,6 @@ public class GearFollower : MonoBehaviour
}
}
private float directionSwitchCooldown = 0.2f;
private float directionSwitchTimer = 0f;
private void UpdateTractionState(Vector2 mousePos)
{
Vector2 gearPos = transform.position;
@@ -144,25 +138,29 @@ public class GearFollower : MonoBehaviour
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); // -180 ~ 180
float angle = Vector2.SignedAngle(forward, toMouse);
// 当角度大于120度,说明鼠标方向明显是反的
if (Mathf.Abs(angle) > 120f)
{
directionSwitchTimer += Time.deltaTime;
@@ -171,7 +169,6 @@ public class GearFollower : MonoBehaviour
tractionDirection *= -1;
directionSwitchTimer = 0f;
// 更新前进段
nextIndex = WrapIndex(currentEdgeIndex + tractionDirection);
from = edgePoints[currentEdgeIndex];
to = edgePoints[nextIndex];
@@ -193,12 +190,8 @@ public class GearFollower : MonoBehaviour
{
transform.position = targetPoint;
// 如果到达段末,前进一段
if (Vector2.Distance(targetPoint, to) < 0.01f)
{
// 记录切割痕迹
AddCutSegment(currentEdgeIndex);
currentEdgeIndex = nextIndex;
}
}
@@ -206,85 +199,43 @@ public class GearFollower : MonoBehaviour
{
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 AddCutSegment(int startIndex)
private void StartCutting()
{
// 防止重复添加同一段(可选)
foreach (var seg in cutSegments)
isCutting = true;
//cutLinePoints.Clear();
if (cutLineRenderer != null)
{
if (seg.startIndex == startIndex)
return;
cutLineRenderer.positionCount = 0;
}
GameObject go = new GameObject($"CutSegment_{startIndex}");
go.transform.parent = transform.parent; // 放在GearFollower同级方便管理
LineRenderer lr = go.AddComponent<LineRenderer>();
lr.positionCount = 4; // 画一个四点的矩形条表示卷边
lr.material = cutEdgeMaterial != null ? cutEdgeMaterial : new Material(Shader.Find("Sprites/Default"));
lr.widthCurve = AnimationCurve.Constant(0, 1, 0.05f);
lr.numCapVertices = 4;
lr.useWorldSpace = true;
CutSegment newSeg = new CutSegment
{
startIndex = startIndex,
timeElapsed = 0f,
lineRenderer = lr
};
cutSegments.Add(newSeg);
}
// 每帧更新所有切割段动画
private void UpdateCutSegments()
private void AddCutPoint(Vector3 point)
{
for (int i = cutSegments.Count - 1; i >= 0; i--)
if (cutLinePoints.Count == 0 || Vector3.Distance(cutLinePoints[cutLinePoints.Count - 1], point) > 0.01f)
{
var seg = cutSegments[i];
seg.timeElapsed += Time.deltaTime;
float expandT = Mathf.Clamp01(seg.timeElapsed / cutEdgeExpandDuration);
float coolT = Mathf.Clamp01((seg.timeElapsed - cutEdgeExpandDuration) / cutEdgeCoolDuration);
// 获取该段两个端点
int nextIndex = WrapIndex(seg.startIndex + 1);
Vector2 p1 = edgePoints[seg.startIndex];
Vector2 p2 = edgePoints[nextIndex];
Vector2 segmentDir = (p2 - p1).normalized;
Vector2 normal = new Vector2(-segmentDir.y, segmentDir.x);
float offset = cutEdgeMaxOffset * expandT;
// 四个点形成的卷边矩形
Vector3[] positions = new Vector3[4];
// 主边
positions[0] = p1;
positions[1] = p2;
// 卷边偏移边(沿法线方向偏移offset)
positions[2] = p2 + normal * offset;
positions[3] = p1 + normal * offset;
seg.lineRenderer.positionCount = 4;
seg.lineRenderer.SetPositions(positions);
// 颜色渐变
Color color = cutEdgeColorGradient.Evaluate(1f - coolT);
seg.lineRenderer.startColor = color;
seg.lineRenderer.endColor = color;
// 冷却结束删除该段
if (seg.timeElapsed > cutEdgeExpandDuration + cutEdgeCoolDuration)
cutLinePoints.Add(point);
if (cutLineRenderer != null)
{
Destroy(seg.lineRenderer.gameObject);
cutSegments.RemoveAt(i);
cutLineRenderer.positionCount = cutLinePoints.Count;
cutLineRenderer.SetPositions(cutLinePoints.ToArray());
}
}
}
private void EndCutting()
{
isCutting = false;
// 这里可以添加切割结束后的淡出效果等
}
private int FindNearestEdgePointIndex(Vector2 pos, out float minDist)
{
int nearestIndex = 0;