using System.Collections.Generic; using Shapes; 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 cutEdgeIndices = new HashSet(); // 记录已切割的边缘点索引 private float currentEmissionRate = 0f; private Vector3 originalLocalPosition; private List edgePoints; private List 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 timedCutPoints = new List(); public float cutLineDelay = 3f; private Vector3 shakeOffset = Vector3.zero; private Vector3 targetPosition = Vector3.zero; private bool isInTractionMode = false; private bool isDrillSfxPlaying = false; // 标记音效是否已播放 void Start() { } public void Init(SpriteRenderer targetSpriteRenderer) { SpriteRenderer gearRenderer = GetComponent(); 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.playOnAwake = false; audioSource.spatialBlend = 0f; // 2D音效 // 确保初始状态 isCutting = false; isInTractionMode = false; if (sparkVFX != null) { sparkVFX.Stop(); } } void Update() { } 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 Debug.Log($"鼠标屏幕坐标: {mouseScreenPos}"); Debug.Log($"鼠标世界坐标: {mouseWorldPos}"); Debug.Log($"齿轮当前位置: {transform.position}"); if (targetSpriteRenderer != null) { Debug.Log($"目标精灵位置: {targetSpriteRenderer.transform.position}"); Debug.Log($"目标精灵边界: {targetSpriteRenderer.bounds}"); } 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(); 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(); originalEdgePoints = new List(); if (targetSpriteRenderer == null) { Debug.LogError("targetSpriteRenderer 为空"); return; } List shapePoints = new List(); 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); Debug.Log($"点 {i} 局部坐标: {localPoint1} -> 世界坐标: {worldPoint1}"); // 计算相对于精灵中心点的缩放 Vector3 scaledPoint1 = spriteWorldPos + (worldPoint1 - spriteWorldPos) * edgePointsScale; Vector3 scaledPoint2 = spriteWorldPos + (worldPoint2 - spriteWorldPos) * edgePointsScale; // 确保缩放后的点也在合理范围内 scaledPoint1 = ClampPosition(scaledPoint1, spriteWorldPos, maxScale); scaledPoint2 = ClampPosition(scaledPoint2, spriteWorldPos, maxScale); Debug.Log($"点 {i} 缩放后世界坐标: {scaledPoint1}"); 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)]; Debug.Log($"前一个点: {prev}, 后一个点: {next}"); float distToPrev = Vector2.Distance(mousePos, prev); float distToNext = Vector2.Distance(mousePos, next); Debug.Log($"到前一个点距离: {distToPrev}, 到后一个点距离: {distToNext}"); tractionDirection = (distToNext < distToPrev) ? 1 : -1; targetPosition = edgePoints[currentEdgeIndex]; transform.position = targetPosition; currentState = State.Traction; isInTractionMode = true; // 设置渲染顺序为5 SpriteRenderer gearRenderer = GetComponent(); 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); if (Mathf.Abs(angle) > 120f) { directionSwitchTimer += Time.deltaTime; if (directionSwitchTimer > directionSwitchCooldown) { 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 maxStep = tractionSpeed * 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 visiblePoints = new List(); foreach (var timedPoint in timedCutPoints) { if (currentTime - timedPoint.timeAdded >= cutLineDelay) { visiblePoints.Add(timedPoint.position); } else { } } 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(); 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(); 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]); } Debug.Log($"计算总边缘长度: {totalEdgeLength}"); } private void UpdateCutProgress(Vector2 from, Vector2 to) { // 计算当前段的切割长度 float segmentLength = Vector2.Distance(from, to); cutLength += segmentLength; // 记录已切割的边缘点 cutEdgeIndices.Add(currentEdgeIndex); // 计算切割进度 float progress = cutLength / totalEdgeLength; Debug.Log($"当前切割长度: {cutLength}, 总长度: {totalEdgeLength}, 进度: {progress * 100}%"); // 更新切割进度 var cuttingManager = FixSystemCenter.SystemDic.Get(); 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 sortedIndices = new List(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(); 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(); 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(); if (gearRenderer != null) { cutCompleteSequence.Join(gearRenderer.DOFade(0f, fadeOutDuration)); } cutCompleteSequence.OnComplete(() => { var cuttingManager = FixSystemCenter.SystemDic.Get(); if (cuttingManager != null) { cuttingManager.UpdateModule(); } }); } private void StartCutTextFlicker() { var cuttingManager = FixSystemCenter.SystemDic.Get(); 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(); } StopDrillSfxIfPlaying(); } // 停止音效的辅助方法 private void StopDrillSfxIfPlaying() { var audioManager = AibisDream.Kit.AudioManager.Instance; if (audioManager != null) { audioManager.StopSfx("event:/FollowInput/drill"); } } }