增加卷边
This commit is contained in:
+176
-21
@@ -207,6 +207,142 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!1 &173508913
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 173508914}
|
||||
- component: {fileID: 173508915}
|
||||
m_Layer: 0
|
||||
m_Name: Trail
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
--- !u!4 &173508914
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 173508913}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1568743344}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!96 &173508915
|
||||
TrailRenderer:
|
||||
serializedVersion: 3
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 173508913}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 0
|
||||
m_LightProbeUsage: 0
|
||||
m_ReflectionProbeUsage: 0
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_Time: 1
|
||||
m_PreviewTimeScale: 1
|
||||
m_Parameters:
|
||||
serializedVersion: 3
|
||||
widthMultiplier: 1
|
||||
widthCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0.027952194
|
||||
value: 0.30451202
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
colorGradient:
|
||||
serializedVersion: 2
|
||||
key0: {r: 1, g: 1, b: 1, a: 1}
|
||||
key1: {r: 1, g: 1, b: 1, a: 1}
|
||||
key2: {r: 0, g: 0, b: 0, a: 0}
|
||||
key3: {r: 0, g: 0, b: 0, a: 0}
|
||||
key4: {r: 0, g: 0, b: 0, a: 0}
|
||||
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||
ctime0: 0
|
||||
ctime1: 65535
|
||||
ctime2: 0
|
||||
ctime3: 0
|
||||
ctime4: 0
|
||||
ctime5: 0
|
||||
ctime6: 0
|
||||
ctime7: 0
|
||||
atime0: 0
|
||||
atime1: 65535
|
||||
atime2: 0
|
||||
atime3: 0
|
||||
atime4: 0
|
||||
atime5: 0
|
||||
atime6: 0
|
||||
atime7: 0
|
||||
m_Mode: 0
|
||||
m_ColorSpace: -1
|
||||
m_NumColorKeys: 2
|
||||
m_NumAlphaKeys: 2
|
||||
numCornerVertices: 0
|
||||
numCapVertices: 0
|
||||
alignment: 0
|
||||
textureMode: 0
|
||||
textureScale: {x: 1, y: 1}
|
||||
shadowBias: 0.5
|
||||
generateLightingData: 0
|
||||
m_MinVertexDistance: 0.1
|
||||
m_MaskInteraction: 0
|
||||
m_Autodestruct: 0
|
||||
m_Emitting: 1
|
||||
m_ApplyActiveColorSpace: 1
|
||||
--- !u!1 &519420028
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1249,10 +1385,11 @@ Transform:
|
||||
m_GameObject: {fileID: 1568743342}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 3.05, y: -0.37, z: 0}
|
||||
m_LocalPosition: {x: 3.66, y: 0.31, z: 0}
|
||||
m_LocalScale: {x: 1.5038, y: 1.5038, z: 1.5038}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Children:
|
||||
- {fileID: 173508914}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1568743345
|
||||
@@ -1269,12 +1406,11 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
targetSpriteRenderer: {fileID: 2110358530}
|
||||
snapDistance: 0.3
|
||||
detachDistance: 2
|
||||
tractionSpeed: 0.6
|
||||
rotationSpeed: 360
|
||||
edgeScaleFactor: 1.2
|
||||
normalEdgeColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
cutEdgeColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1}
|
||||
detachDistance: 10
|
||||
tractionSpeed: 1
|
||||
cutLineRenderer: {fileID: 1568743346}
|
||||
cutLineWidth: 0.1
|
||||
noiseAmount: 0.05
|
||||
--- !u!120 &1568743346
|
||||
LineRenderer:
|
||||
serializedVersion: 2
|
||||
@@ -1518,7 +1654,6 @@ GameObject:
|
||||
- component: {fileID: 2110358529}
|
||||
- component: {fileID: 2110358530}
|
||||
- component: {fileID: 2110358532}
|
||||
- component: {fileID: 2110358531}
|
||||
m_Layer: 0
|
||||
m_Name: Module Pic
|
||||
m_TagString: Cuttable
|
||||
@@ -1593,18 +1728,6 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!114 &2110358531
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2110358528}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ad912e1febe29ed499461c26b73ac2b8, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!60 &2110358532
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1688,6 +1811,37 @@ PolygonCollider2D:
|
||||
- {x: 0.5674419, y: -0.4883721}
|
||||
- {x: 0.572093, y: -0.43255815}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2135342729
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2135342730}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &2135342730
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2135342729}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.5953367, y: -2.2389753, z: 0.43786627}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1698,3 +1852,4 @@ SceneRoots:
|
||||
- {fileID: 1568743344}
|
||||
- {fileID: 1598025876}
|
||||
- {fileID: 1599995100}
|
||||
- {fileID: 2135342730}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user