Files
aibis-dream/Assets/Scripts/FixSystemNew/BodyModule/GearFollower.cs
T

918 lines
31 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 UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.VFX;
using DG.Tweening;
using AibisDream.FixSystem;
using AibisDream; // 添加OpenSystem的命名空间
using System.Collections;
using AibisDream.Kit;
public class GearFollower : MonoBehaviour
{
[Header("参数")]
public SpriteRenderer targetSpriteRenderer;
public float snapDistance = 1.0f;
public float detachDistance = 1.0f;
public float tractionSpeed = 2f;
public float edgePointsScale = 1.2f; // 边缘点缩放倍率
[Header("切割轨迹")]
public LineRenderer cutLineRenderer;
public float cutLineWidth = 0.1f;
public float noiseAmount = 0.05f; // 毛刺偏移幅度
public float segmentDuration = 4f; // 线段持续时间
[Header("震动与光照反馈")]
public Light2D cutLight; // 需要使用 Unity 的 2D 点光源(URP
public float lightPulseSpeed = 5f;
public float lightMaxIntensity = 1.5f;
public float shakeAmount = 0.05f;
[Header("VFX火花")]
public VisualEffect sparkVFX; // 关联Spark VFX Graph组件
public float maxEmissionRate = 50f; // 最大发射速率
public float emissionChangeSpeed = 100f; // 发射速率变化速度
[Header("切割完成效果")]
public float cutCompletionThreshold = 0.95f; // 切割完成阈值(切割路径占边缘的比例)
public float minCutLength = 0.5f; // 最小切割长度(相对于总长度的比例)
public float targetShakeAmount = 0.02f; // 目标抖动幅度
public float cutCompleteTiltAngle = 15f; // 切割完成后的倾斜角度
public float cutCompleteOffset = 0.2f; // 切割完成后的位移
public float cutCompleteScale = 1.2f; // 切割完成后的放大倍数
public float fadeOutDuration = 1f; // 淡出持续时间
public float cutCompleteDelay = 1f; // 切割完成后的延迟时间
public float scaleUpDuration = 1f; // 放大动画持续时间
public float fadeOutDelay = 1f; // 淡出前的延迟时间
public AudioClip cutCompleteSound; // 切割完成音效
[Header("CutText闪烁效果")]
private float cutTextFlickerThreshold = 0.7f; // 开始闪烁的阈值
private float cutTextFlickerInterval = 0.1f; // 闪烁间隔
private float cutTextFlickerTimer = 0f; // 闪烁计时器
private bool isCutTextFlickering = false; // 是否正在闪烁
private float totalEdgeLength = 0f; // 总边缘长度
private float cutLength = 0f; // 已切割长度
private bool isCutComplete = false;
private bool isFadingOut = false;
private float fadeOutTimer = 0f;
private Vector3 originalTargetPosition;
private Quaternion originalTargetRotation;
private Vector3 originalTargetScale;
private AudioSource audioSource;
private DG.Tweening.Sequence cutCompleteSequence; // DOTween序列
private HashSet<int> cutEdgeIndices = new HashSet<int>(); // 记录已切割的边缘点索引
private float currentEmissionRate = 0f;
private Vector3 originalLocalPosition;
private List<Vector2> edgePoints;
private List<Vector2> originalEdgePoints; // 存储原始边缘点
private int currentEdgeIndex = 0;
private int tractionDirection = 1;
private enum State { Follow, Traction }
private State currentState = State.Follow;
private Camera mainCamera;
private bool isCutting = false;
private float directionSwitchCooldown = 0.2f;
private float directionSwitchTimer = 0f;
private class TimedPoint
{
public Vector3 position;
public float timeAdded;
public TimedPoint(Vector3 pos, float time)
{
position = pos;
timeAdded = time;
}
}
private List<TimedPoint> timedCutPoints = new List<TimedPoint>();
public float cutLineDelay = 3f;
private Vector3 shakeOffset = Vector3.zero;
private Vector3 targetPosition = Vector3.zero;
private bool isInTractionMode = false;
private bool isDrillSfxPlaying = false; // 标记音效是否已播放
public void Init(SpriteRenderer targetSpriteRenderer)
{
SpriteRenderer gearRenderer = GetComponent<SpriteRenderer>();
if (gearRenderer != null)
{
gearRenderer.color = new Color(1, 1, 1, 1);
}
this.targetSpriteRenderer = null;
this.targetSpriteRenderer = targetSpriteRenderer;
mainCamera = Camera.main;
LoadEdgePointsFromSprite();
CalculateTotalEdgeLength();
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
cutLineRenderer.widthCurve = AnimationCurve.Constant(0, 1, cutLineWidth);
cutLineRenderer.material = new Material(Shader.Find("Sprites/Default"));
cutLineRenderer.numCapVertices = 0;
cutLineRenderer.sortingOrder = 1;
}
if (cutLight != null)
{
cutLight.intensity = 0f;
cutLight.enabled = false;
}
// 初始化目标相关变量
if (targetSpriteRenderer != null)
{
originalTargetPosition = targetSpriteRenderer.transform.position;
originalTargetRotation = targetSpriteRenderer.transform.rotation;
originalTargetScale = targetSpriteRenderer.transform.localScale;
}
// 添加音频源组件
audioSource = gameObject.AddComponent<AudioSource>();
audioSource.playOnAwake = false;
audioSource.spatialBlend = 0f; // 2D音效
// 确保初始状态
isCutting = false;
isInTractionMode = false;
if (sparkVFX != null)
{
sparkVFX.Stop();
}
}
public void GearUpdate()
{
// 获取鼠标在屏幕上的位置
Vector3 mouseScreenPos = Input.mousePosition;
// 将屏幕坐标转换为世界坐标
Vector3 mouseWorldPos = mainCamera.ScreenToWorldPoint(new Vector3(mouseScreenPos.x, mouseScreenPos.y, -mainCamera.transform.position.z));
mouseWorldPos.z = 0; // 确保z坐标为0
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;
}
UpdateCutLineRenderer();
UpdateVisualFeedback();
}
private void UpdateVisualFeedback()
{
if (isInTractionMode && isCutting)
{
// 原始小幅震动
Vector2 baseShake = Random.insideUnitCircle * shakeAmount;
shakeOffset = (Vector3)baseShake;
transform.position = targetPosition + shakeOffset;
// 目标抖动
if (targetSpriteRenderer != null && !isCutComplete)
{
Vector2 targetShake = Random.insideUnitCircle * targetShakeAmount;
targetSpriteRenderer.transform.position = originalTargetPosition + (Vector3)targetShake;
}
// 根据震动幅度控制灯光强度
if (cutLight != null)
{
cutLight.enabled = true;
float intensity = shakeOffset.magnitude / shakeAmount; // 得到 [0,1] 比例
cutLight.intensity = intensity * lightMaxIntensity;
}
}
else
{
shakeOffset = Vector3.zero;
if (isInTractionMode)
{
transform.position = targetPosition;
}
if (cutLight != null)
{
cutLight.intensity = 0f;
cutLight.enabled = false;
}
}
// 处理淡出效果
if (isFadingOut)
{
fadeOutTimer += Time.deltaTime;
float alpha = 1f - (fadeOutTimer / fadeOutDuration);
if (targetSpriteRenderer != null)
{
Color color = targetSpriteRenderer.color;
color.a = alpha;
targetSpriteRenderer.color = color;
}
// 齿轮也淡出
SpriteRenderer gearRenderer = GetComponent<SpriteRenderer>();
if (gearRenderer != null)
{
Color gearColor = gearRenderer.color;
gearColor.a = alpha;
gearRenderer.color = gearColor;
}
if (fadeOutTimer >= fadeOutDuration)
{
// 淡出完成后销毁物体
Destroy(gameObject);
if (targetSpriteRenderer != null)
{
Destroy(targetSpriteRenderer.gameObject);
}
}
}
}
private void LoadEdgePointsFromSprite()
{
edgePoints = new List<Vector2>();
originalEdgePoints = 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;
}
Debug.Log($"原始形状点数量: {shapePoints.Count}");
float desiredSegmentLength = 0.1f;
edgePoints.Clear();
originalEdgePoints.Clear();
// 获取精灵的世界坐标
Vector3 spriteWorldPos = targetSpriteRenderer.transform.position;
Debug.Log($"精灵世界坐标: {spriteWorldPos}");
// 计算精灵的局部边界
Bounds localBounds = targetSpriteRenderer.bounds;
Vector3 localCenter = localBounds.center;
Vector3 localSize = localBounds.size;
// 计算缩放后的边界
float maxScale = Mathf.Max(localSize.x, localSize.y) * edgePointsScale;
float minScale = Mathf.Min(localSize.x, localSize.y) * edgePointsScale;
Debug.Log($"精灵局部边界: 中心={localCenter}, 大小={localSize}");
Debug.Log($"缩放后边界: 最大={maxScale}, 最小={minScale}");
for (int i = 0; i < shapePoints.Count; i++)
{
// 将局部坐标转换为世界坐标
Vector3 localPoint1 = shapePoints[i];
Vector3 localPoint2 = shapePoints[(i + 1) % shapePoints.Count];
// 转换为世界坐标
Vector3 worldPoint1 = targetSpriteRenderer.transform.TransformPoint(localPoint1);
Vector3 worldPoint2 = targetSpriteRenderer.transform.TransformPoint(localPoint2);
// 确保坐标在合理范围内
worldPoint1 = ClampPosition(worldPoint1, spriteWorldPos, maxScale);
worldPoint2 = ClampPosition(worldPoint2, spriteWorldPos, maxScale);
// 计算相对于精灵中心点的缩放
Vector3 scaledPoint1 = spriteWorldPos + (worldPoint1 - spriteWorldPos) * edgePointsScale;
Vector3 scaledPoint2 = spriteWorldPos + (worldPoint2 - spriteWorldPos) * edgePointsScale;
// 确保缩放后的点也在合理范围内
scaledPoint1 = ClampPosition(scaledPoint1, spriteWorldPos, maxScale);
scaledPoint2 = ClampPosition(scaledPoint2, spriteWorldPos, maxScale); ;
float segmentLength = Vector3.Distance(scaledPoint1, scaledPoint2);
int steps = Mathf.Max(1, Mathf.CeilToInt(segmentLength / desiredSegmentLength));
for (int s = 0; s < steps; s++)
{
float t = (float)s / steps;
Vector3 interpolated = Vector3.Lerp(scaledPoint1, scaledPoint2, t);
Vector3 originalInterpolated = Vector3.Lerp(worldPoint1, worldPoint2, t);
// 确保插值点也在合理范围内
interpolated = ClampPosition(interpolated, spriteWorldPos, maxScale);
originalInterpolated = ClampPosition(originalInterpolated, spriteWorldPos, maxScale);
// 存储为Vector2,但保持世界坐标
edgePoints.Add(new Vector2(interpolated.x, interpolated.y));
originalEdgePoints.Add(new Vector2(originalInterpolated.x, originalInterpolated.y));
}
}
if ((edgePoints[0] - edgePoints[edgePoints.Count - 1]).sqrMagnitude > 0.0001f)
{
edgePoints.Add(edgePoints[0]);
originalEdgePoints.Add(originalEdgePoints[0]);
}
Debug.Log($"生成的边缘点数量: {edgePoints.Count}");
Debug.Log($"第一个边缘点世界坐标: {edgePoints[0]}");
Debug.Log($"最后一个边缘点世界坐标: {edgePoints[edgePoints.Count - 1]}");
}
// 添加一个辅助方法来限制坐标范围
private Vector3 ClampPosition(Vector3 position, Vector3 center, float maxDistance)
{
Vector3 offset = position - center;
float distance = offset.magnitude;
if (distance > maxDistance)
{
offset = offset.normalized * maxDistance;
position = center + offset;
}
return position;
}
private void UpdateFollowState(Vector2 mousePos)
{
transform.position = mousePos;
isInTractionMode = false;
int nearestIndex = FindNearestEdgePointIndex(mousePos, out float dist);
Debug.Log($"最近边缘点索引: {nearestIndex}, 距离: {dist}");
Debug.Log($"最近边缘点位置: {edgePoints[nearestIndex]}");
Debug.Log($"当前snapDistance: {snapDistance}");
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;
targetPosition = edgePoints[currentEdgeIndex];
transform.position = targetPosition;
currentState = State.Traction;
isInTractionMode = true;
// 设置渲染顺序为5
SpriteRenderer gearRenderer = GetComponent<SpriteRenderer>();
if (gearRenderer != null)
{
gearRenderer.sortingOrder = 5;
} // 只在未播放时播放音效
}
}
private void UpdateTractionState(Vector2 mousePos)
{
Vector2 gearPos = transform.position - shakeOffset;
if (Vector2.Distance(mousePos, gearPos) > detachDistance)
{
EndCutting();
return;
}
if (!Input.GetMouseButton(0))
{
EndCutting();
return;
}
if (!isCutting && Input.GetMouseButton(0))
{
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);
// 优化:计算当前段的角度,判断是否为尖角
int prevIndex = WrapIndex(currentEdgeIndex - tractionDirection);
Vector2 prevPoint = edgePoints[prevIndex];
Vector2 prevSegment = (from - prevPoint).normalized;
float segmentAngle = Vector2.Angle(prevSegment, forward);
bool isSharpCorner = segmentAngle > 60f; // 夹角大于60度认为是尖角
// 动态调整方向切换阈值和冷却时间
float switchAngleThreshold = isSharpCorner ? 90f : 120f;
float currentCooldown = isSharpCorner ? 0.05f : directionSwitchCooldown;
if (Mathf.Abs(angle) > switchAngleThreshold)
{
directionSwitchTimer += Time.deltaTime;
if (directionSwitchTimer > currentCooldown)
{
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 speedMultiplier = isSharpCorner ? 1.5f : 1.0f;
float maxStep = tractionSpeed * speedMultiplier * Time.deltaTime;
if (distance <= maxStep)
{
targetPosition = targetPoint;
UpdateCutProgress(from, targetPoint); // 更新切割进度
if (Vector2.Distance(targetPoint, to) < 0.01f)
{
currentEdgeIndex = nextIndex;
}
}
else
{
targetPosition = (Vector3)gearPos + (Vector3)(moveDir.normalized * maxStep);
}
transform.position = targetPosition + shakeOffset;
// 使用原始边缘点来记录切割线
Vector2 originalFrom = originalEdgePoints[currentEdgeIndex];
Vector2 originalTo = originalEdgePoints[nextIndex];
Vector2 originalTargetPoint = GetClosestPointOnSegment(originalFrom, originalTo, transform.position - shakeOffset);
// 记录切割点(带噪声模拟毛糙)
Vector3 noisyPoint = originalTargetPoint;
noisyPoint.x += Random.Range(-noiseAmount, noiseAmount);
noisyPoint.y += Random.Range(-noiseAmount, noiseAmount);
AddCutPoint(noisyPoint);
if (sparkVFX != null)
{
// 计算从切割点到目标精灵中心的向量
Vector2 center = targetSpriteRenderer.transform.position;
Vector2 toCenter = center - (Vector2)originalTargetPoint;
// 计算法线方向(垂直于切割方向)
Vector2 normal = Vector2.Perpendicular(moveDir.normalized);
// 根据切割方向决定法线方向
if (tractionDirection > 0)
{
normal = -normal;
}
Vector3 velocity = (Vector3)normal * 10f;
sparkVFX.SetVector3("Initial Velocity1", velocity);
}
}
private void StartCutting()
{
isCutting = true;
//cutLinePoints.Clear();
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
}
if (sparkVFX != null)
{
sparkVFX.Play();
}
AudioManager.Instance.SetSfxParam("event:/FollowInput/drill","is_drill_cutting",1f);
}
private void AddCutPoint(Vector3 point)
{
if (timedCutPoints.Count == 0 || Vector3.Distance(timedCutPoints[timedCutPoints.Count - 1].position, point) > 0.05f)
{
timedCutPoints.Add(new TimedPoint(point, Time.time));
// 更新光照位置到最新切点
if (cutLight != null)
{
cutLight.transform.position = point;
}
// 把火花特效位置设置到当前切割点
if (sparkVFX != null)
{
sparkVFX.transform.position = point;
}
if (timedCutPoints.Count >= 2)
{
Vector3 prev = timedCutPoints[timedCutPoints.Count - 2].position;
Vector3 curr = timedCutPoints[timedCutPoints.Count - 1].position;
CreateSegment(prev, curr);
}
}
}
private void UpdateCutLineRenderer()
{
if (cutLineRenderer == null) return;
float currentTime = Time.time;
List<Vector3> visiblePoints = new List<Vector3>();
foreach (var timedPoint in timedCutPoints)
{
if (currentTime - timedPoint.timeAdded >= cutLineDelay)
{
visiblePoints.Add(timedPoint.position);
}
}
cutLineRenderer.positionCount = visiblePoints.Count;
if (visiblePoints.Count > 0)
cutLineRenderer.SetPositions(visiblePoints.ToArray());
}
private void EndCutting()
{
isCutting = false;
if (sparkVFX != null)
{
sparkVFX.Stop();
}
// AudioManager.Instance.SetGlobalParam("is_drill_cutting", 0f);
AudioManager.Instance.SetSfxParam("event:/FollowInput/drill","is_drill_cutting",0f);
// 关闭切割光照
if (cutLight != null)
{
cutLight.enabled = false;
cutLight.intensity = 0f;
}
// 停止音效
}
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;
}
private void CreateSegment(Vector3 from, Vector3 to)
{
GameObject segObj = new GameObject("HeatLineSegment");
segObj.transform.parent = this.transform;
LineRenderer segLine = segObj.AddComponent<LineRenderer>();
segLine.positionCount = 2;
segLine.SetPosition(0, from);
segLine.SetPosition(1, to);
segLine.sortingOrder = 4;
segLine.material = new Material(Shader.Find("Sprites/Default"));
segLine.widthCurve = AnimationCurve.Constant(0, 1, cutLineWidth);
segLine.numCapVertices = 0;
var segmentController = segObj.AddComponent<HeatLineSegmentController>();
segmentController.duration = segmentDuration;
}
private void CalculateTotalEdgeLength()
{
totalEdgeLength = 0f;
for (int i = 0; i < edgePoints.Count - 1; i++)
{
totalEdgeLength += Vector2.Distance(edgePoints[i], edgePoints[i + 1]);
}
}
private void UpdateCutProgress(Vector2 from, Vector2 to)
{
// 计算当前段的切割长度
float segmentLength = Vector2.Distance(from, to);
cutLength += segmentLength;
// 记录已切割的边缘点
cutEdgeIndices.Add(currentEdgeIndex);
// 计算切割进度
float progress = cutLength / totalEdgeLength;
// 更新切割进度
var cuttingManager = FixSystemCenter.SystemDic.Get<CuttingManager>();
if (cuttingManager != null)
{
cuttingManager.UpdateCutProgress(progress);
}
// 检查是否满足切割完成条件
if (!isCutComplete && progress >= cutCompletionThreshold)
{
// 检查是否形成了连续的切割路径
bool hasContinuousCut = CheckContinuousCut();
Debug.Log($"连续切割检查: {hasContinuousCut}, 已切割点数: {cutEdgeIndices.Count}, 总点数: {edgePoints.Count}");
if (hasContinuousCut)
{
isCutComplete = true;
OnCutComplete();
}
}
}
private bool CheckContinuousCut()
{
if (cutEdgeIndices.Count < 2) return false;
// 将索引转换为有序列表
List<int> sortedIndices = new List<int>(cutEdgeIndices);
sortedIndices.Sort();
// 检查是否有连续的切割点
int continuousCount = 1;
int maxContinuousCount = 1;
int totalPoints = edgePoints.Count;
// 检查所有可能的连续段
for (int startIndex = 0; startIndex < sortedIndices.Count; startIndex++)
{
continuousCount = 1;
int currentIndex = sortedIndices[startIndex];
// 向前检查
for (int i = 1; i < sortedIndices.Count; i++)
{
int nextIndex = (currentIndex + i) % totalPoints;
if (sortedIndices.Contains(nextIndex))
{
continuousCount++;
}
else
{
break;
}
}
// 向后检查
for (int i = 1; i < sortedIndices.Count; i++)
{
int prevIndex = (currentIndex - i + totalPoints) % totalPoints;
if (sortedIndices.Contains(prevIndex))
{
continuousCount++;
}
else
{
break;
}
}
maxContinuousCount = Mathf.Max(maxContinuousCount, continuousCount);
}
// 计算连续切割点的比例
float continuousRatio = (float)maxContinuousCount / totalPoints;
Debug.Log($"最大连续切割点数: {maxContinuousCount}, 总点数: {totalPoints}, 比例: {continuousRatio * 100}%");
// 如果连续切割点的数量超过总点数的70%,认为形成了有效的切割路径
return continuousRatio >= 0.7f;
}
private void OnCutComplete()
{
var cuttingManager = FixSystemCenter.SystemDic.Get<CuttingManager>();
if (cuttingManager != null)
{
cuttingManager.OnCutComplete();
}
}
public void ClearCutLine()
{
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
Destroy(this.gameObject);
}
}
public void FinishCutting()
{
// 清理特效
if (cutLight != null)
{
cutLight.enabled = false;
}
if (sparkVFX != null)
{
sparkVFX.Stop();
}
// 停止切割音效
var audioManager = AibisDream.Kit.AudioManager.Instance;
if (audioManager != null)
{
// 快速将is_drill_cutting参数从1拉到0
AudioManager.Instance.SetSfxParam("event:/FollowInput/drill","is_drill_cutting",0f);
}
// 计算随机方向
float randomAngle = Random.Range(-cutCompleteTiltAngle, cutCompleteTiltAngle);
Vector2 randomOffset = Random.insideUnitCircle * cutCompleteOffset;
var cuttingManager = FixSystemCenter.SystemDic.Get<CuttingManager>();
if (cuttingManager != null)
{
cuttingManager.StopCutTextFlicker();
cuttingManager.SetCutTextVisible(false);
}
// 立即应用倾斜和位移
AudioManager.Instance.PlaySfx("event:/ActionFB/mod_cutout");
targetSpriteRenderer.transform.rotation = Quaternion.Euler(0, 0, randomAngle);
targetSpriteRenderer.transform.position = originalTargetPosition + (Vector3)randomOffset;
// 创建动画序列
cutCompleteSequence = DOTween.Sequence();
// 等待一段时间后开始放大
cutCompleteSequence.AppendInterval(cutCompleteDelay);
cutCompleteSequence.Append(targetSpriteRenderer.transform.DOScale(originalTargetScale * cutCompleteScale, scaleUpDuration)
.SetEase(Ease.OutQuad));
// 等待一段时间后开始淡出
cutCompleteSequence.AppendInterval(fadeOutDelay);
cutCompleteSequence.Append(targetSpriteRenderer.DOFade(0f, fadeOutDuration));
// 齿轮也同时淡出
SpriteRenderer gearRenderer = GetComponent<SpriteRenderer>();
if (gearRenderer != null)
{
cutCompleteSequence.Join(gearRenderer.DOFade(0f, fadeOutDuration));
}
cutCompleteSequence.OnComplete(() =>
{
var cuttingManager = FixSystemCenter.SystemDic.Get<CuttingManager>();
if (cuttingManager != null)
{
cuttingManager.UpdateModule();
}
});
}
private void StartCutTextFlicker()
{
var cuttingManager = FixSystemCenter.SystemDic.Get<CuttingManager>();
if (cuttingManager != null)
{
// 开始闪烁效果
StartCoroutine(CutTextFlickerCoroutine(cuttingManager));
}
}
private IEnumerator CutTextFlickerCoroutine(CuttingManager cuttingManager)
{
while (isCutTextFlickering && !isCutComplete)
{
// 随机决定是否显示
bool shouldShow = Random.value > 0.5f;
cuttingManager.SetCutTextVisible(shouldShow);
// 随机等待时间,模拟接触不良效果
float waitTime = Random.Range(cutTextFlickerInterval * 0.5f, cutTextFlickerInterval * 1.5f);
yield return new WaitForSeconds(waitTime);
}
// 确保最终状态是可见的
if (!isCutComplete)
{
cuttingManager.SetCutTextVisible(true);
}
}
private void OnDestroy()
{
// 清理DOTween序列
if (cutCompleteSequence != null)
{
cutCompleteSequence.Kill();
cutCompleteSequence = null;
}
// 停止所有协程
StopAllCoroutines();
// 清理特效
if (cutLight != null)
{
cutLight.enabled = false;
}
if (sparkVFX != null)
{
sparkVFX.Stop();
}
// 清理切割线
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
}
// 停止音效
StopDrillSfxIfPlaying();
// 重置目标精灵状态
if (targetSpriteRenderer != null)
{
// 恢复原始状态
targetSpriteRenderer.transform.position = originalTargetPosition;
targetSpriteRenderer.transform.rotation = originalTargetRotation;
targetSpriteRenderer.transform.localScale = originalTargetScale;
// 恢复透明度
Color color = targetSpriteRenderer.color;
color.a = 1f;
targetSpriteRenderer.color = color;
}
// 重置所有状态变量
isCutting = false;
isInTractionMode = false;
isCutComplete = false;
isFadingOut = false;
isCutTextFlickering = false;
currentState = State.Follow;
// 清理列表
if (timedCutPoints != null)
{
timedCutPoints.Clear();
}
if (cutEdgeIndices != null)
{
cutEdgeIndices.Clear();
}
if (edgePoints != null)
{
edgePoints.Clear();
}
if (originalEdgePoints != null)
{
originalEdgePoints.Clear();
}
}
// 停止音效的辅助方法
private void StopDrillSfxIfPlaying()
{
AudioManager.Instance?.StopSfx("event:/FollowInput/drill");
}
}