diff --git a/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs b/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs index 06cc1057a..d9b3a6459 100644 --- a/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs +++ b/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs @@ -159,7 +159,7 @@ namespace AibisDream [YarnCommand("RetractCable")] public static void RetractCable() { - CablePanel.PullUpCable(); + CablePanel?.PullUpCable(); } [YarnCommand("StartHeadBreathing")] diff --git a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs index cd598e842..643377b7f 100644 --- a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs +++ b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs @@ -36,6 +36,10 @@ namespace AibisDream.FixSystem #region 系统状态 public bool isCableRetracted = true; // 线缆是否收起 + // 与 isCableRetracted(当前可交互状态)分开记录最后一次指令的目标, + // 防止相反方向或外部 DOKill 触发旧动画回调,把线缆恢复到错误状态。 + private bool targetCableRetracted = true; + [SerializeField] private Transform retractedPos; // 收起位置 private ISocket curSocket; @@ -86,6 +90,8 @@ namespace AibisDream.FixSystem private void InitCableVisualFromPrefab() { + targetCableRetracted = isCableRetracted; + if (isCableRetracted) { SetRetractCable(); @@ -145,6 +151,8 @@ namespace AibisDream.FixSystem public void DropDownCableImmediate() { + targetCableRetracted = false; + PlugRef.transform.DOKill(false); PlugRef.PullUpSocketSilent(); CableReelRef.ResetRotation(); PlugRef.RestoreAtStartPositionImmediate(); @@ -212,14 +220,25 @@ namespace AibisDream.FixSystem } } + /// + /// 从插孔拔出(若已插入),放回初始可插线位:线缆放下、可拖拽,不收起。 + /// public void ResetPlug() { - if (IsPlugInSocket()) + bool wasPlugged = IsPlugInSocket(); + if (wasPlugged) { PlugRef.PullUpSocket(); } BodyModuleSystem.CloseModuleSlots(null); + + // 已在初始可插线位且未收起:只关槽,不重复放线/音效 + if (!wasPlugged && !isCableRetracted && !targetCableRetracted) + { + return; + } + DropDownCableImmediate(); AudioManager.Instance.PlaySfx("event:/ActionFB/mods_close"); @@ -313,45 +332,71 @@ namespace AibisDream.FixSystem return BodyModuleSystem.TryFindClosestModule(sourcePos, out targetModule); } + /// 收起线缆:无论是否插在模块上,统一收到线盘位。 public void PullUpCable() { - if (isCableRetracted) return; - - if (IsPlugInSocket()) + bool wasPlugged = IsPlugInSocket(); + if (wasPlugged) { PlugRef.PullUpSocket(); } BodyModuleSystem.CloseModuleSlots(null); - // 收线动画:绳身按纯插值收拢到收纳位(不走拖拽物理,避免其阻尼参数在极短时长内来不及收敛而甩鞭), - // 插头 DOMove 与绳收拢共用同一时长,动画结束时绳已真正收完,再切换到收起态不会有可见的切割感 + // 已在收起或正在收起,且本次无需拔插:不重复收线动画/音效 + if (targetCableRetracted && !wasPlugged) + { + isCableRetracted = true; + return; + } + + targetCableRetracted = true; + isCableRetracted = true; + + PlugRef.transform.DOKill(false); + + if (physicCableRef.State == PhysicCable.CableState.Hidden) + { + ApplyRetractedState(); + return; + } + suppressPlugFollow = true; physicCableRef.CollapseTo(retractedPos.position, RetractDuration); PlugRef.transform.DOMove(retractedPos.position, RetractDuration) - .OnComplete(() => - { - ApplyRetractedState(); - isCableRetracted = true; - }); + .OnComplete(CompleteRetractIfCurrent) + .OnKill(CompleteRetractIfCurrent); AudioManager.Instance.PlaySfx("event:/ActionFB/mods_close"); AudioManager.Instance.PlaySfx("event:/FollowInput/plugdrop"); } + private void CompleteRetractIfCurrent() + { + if (!targetCableRetracted) return; + ApplyRetractedState(); + isCableRetracted = true; + } + public void DropDownCable() { - if (!isCableRetracted) return; - // 确保 Plug 已脱离 Socket(收起状态下通常已脱离,此处为防御性调用) + if (!isCableRetracted && !targetCableRetracted) return; + + targetCableRetracted = false; PlugRef.PullUpSocket(); + PlugRef.transform.DOKill(false); CableReelRef.ResetRotation(); suppressPlugFollow = true; - PlugRef.transform.DOMove(PlugRef.GetStartPos(), 0.1f).OnComplete(() => - { - suppressPlugFollow = false; - physicCableRef.ShowFree(true); - isCableRetracted = false; - }); + PlugRef.transform.DOMove(PlugRef.GetStartPos(), 0.1f).OnComplete(CompleteDropIfCurrent) + .OnKill(CompleteDropIfCurrent); + } + + private void CompleteDropIfCurrent() + { + if (targetCableRetracted) return; + suppressPlugFollow = false; + physicCableRef.ShowFree(true); + isCableRetracted = false; } /// 应用收起状态的视觉与组件设置(不含动画、不含 isCableRetracted 更新) @@ -360,8 +405,6 @@ namespace AibisDream.FixSystem physicCableRef.Hide(); suppressPlugFollow = false; - // 强制收起时长可能短于拔出反馈动画(挤压 punch scale),未播完就会被打断, - // 收起完成后强制清掉,避免插头带着没播完的形变停在收纳位 PlugRef.CancelFeedbackTween(); PlugRef.transform.position = retractedPos.position; @@ -374,8 +417,10 @@ namespace AibisDream.FixSystem public void SetRetractCable() { - ApplyRetractedState(); + targetCableRetracted = true; isCableRetracted = true; + PlugRef.transform.DOKill(false); + ApplyRetractedState(); } #region 灯光系统 - 供外部调用 diff --git a/Assets/Scripts/FixSystemNew/States/CoreFixCues.cs b/Assets/Scripts/FixSystemNew/States/CoreFixCues.cs index ae0a3a799..997b92879 100644 --- a/Assets/Scripts/FixSystemNew/States/CoreFixCues.cs +++ b/Assets/Scripts/FixSystemNew/States/CoreFixCues.cs @@ -84,7 +84,7 @@ namespace AibisDream.FixSystem } else { - FixPanelSystem.GetRepairPanel().ResetPlug(); + FixPanelSystem.GetRepairPanel()?.ResetPlug(); yield return CameraKit.Instance.SwitchCamera(CameraEnum.BodyModule); } @@ -104,7 +104,7 @@ namespace AibisDream.FixSystem bodyModuleSystem.SwitchModuleGroup(moduleState); } - FixPanelSystem.GetRepairPanel().ResetPlug(); + FixPanelSystem.GetRepairPanel()?.ResetPlug(); yield return CameraKit.Instance.SwitchCamera(CameraEnum.BodyModule, true); BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.BodyModule); @@ -117,9 +117,7 @@ namespace AibisDream.FixSystem public IEnumerator Exit(FixTransition transition) { var bodyModuleSystem = FixSystemCenter.SystemDic.Get(); - // 拔出插头 - FixPanelSystem.GetRepairPanel().ResetPlug(); - // 关闭交互 + FixPanelSystem.GetRepairPanel()?.PullUpCable(); bodyModuleSystem.DisableSystem(); yield break; } diff --git a/Assets/Scripts/FixSystemNew/States/CutFixCues.cs b/Assets/Scripts/FixSystemNew/States/CutFixCues.cs index 28476e628..a6810382f 100644 --- a/Assets/Scripts/FixSystemNew/States/CutFixCues.cs +++ b/Assets/Scripts/FixSystemNew/States/CutFixCues.cs @@ -23,7 +23,7 @@ namespace AibisDream.FixSystem // Cut的气泡统一放在BodyModule下 BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Cut); - FixPanelSystem.GetRepairPanel().ResetPlug(); + FixPanelSystem.GetRepairPanel()?.PullUpCable(); var bodyModule = bodyModuleSystem.FindBodyModuleByName("Emo"); if (bodyModule != null) { @@ -70,7 +70,7 @@ namespace AibisDream.FixSystem // Cut的气泡统一放在BodyModule下 BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Cut); - FixPanelSystem.GetRepairPanel().ResetPlug(); + FixPanelSystem.GetRepairPanel()?.PullUpCable(); var bodyModule = bodyModuleSystem.FindBodyModuleByName("Memory"); if (bodyModule != null) { @@ -117,6 +117,7 @@ namespace AibisDream.FixSystem // Cut的气泡统一放在BodyModule下 BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Cut); + FixPanelSystem.GetRepairPanel()?.PullUpCable(); var bodyModule = bodyModuleSystem.FindBodyModuleByName("Logic"); if (bodyModule != null) { diff --git a/Assets/Scripts/FixSystemNew/States/HuoShanFixCues.cs b/Assets/Scripts/FixSystemNew/States/HuoShanFixCues.cs index 8ebe2186b..3396821fe 100644 --- a/Assets/Scripts/FixSystemNew/States/HuoShanFixCues.cs +++ b/Assets/Scripts/FixSystemNew/States/HuoShanFixCues.cs @@ -41,8 +41,8 @@ namespace AibisDream.FixSystem var bodyModuleSystem = FixSystemCenter.SystemDic.Get(); yield return UIManager.Instance.GetPanel().FadeInAsync(0.5f); - // 重置身体模块 - FixPanelSystem.GetRepairPanel().ResetPlug(); + // 离开表达深入:保持线缆收起,回到 BodyModule 后由 Yarn DropCable 再放线 + FixPanelSystem.GetRepairPanel()?.PullUpCable(); // 关闭表达系统视图 expressionManager.CloseView(); @@ -77,7 +77,7 @@ namespace AibisDream.FixSystem public IEnumerator Exit(FixTransition transition) { - FixPanelSystem.GetRepairPanel().ResetPlug(); + FixPanelSystem.GetRepairPanel()?.PullUpCable(); yield break; } } @@ -98,8 +98,7 @@ namespace AibisDream.FixSystem yield return CameraKit.Instance.SwitchCamera(CameraEnum.BodyModule); } - // 插头回到初始插孔 - FixPanelSystem.GetRepairPanel().ResetPlug(); + FixPanelSystem.GetRepairPanel()?.PullUpCable(); // 切换到分析模式相机 yield return UIManager.Instance.GetPanel().FadeInAsync(0.5f); diff --git a/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs b/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs index a7f50b802..028b913e4 100644 --- a/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs +++ b/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs @@ -21,9 +21,7 @@ namespace AibisDream.FixSystem yield return CameraKit.Instance.SwitchCamera(CameraEnum.BodyModule); } - // 插头回到初始插孔 - FixPanelSystem.GetRepairPanel().ResetPlug(); - // 切换相机 + FixPanelSystem.GetRepairPanel()?.PullUpCable(); yield return CameraKit.Instance.SwitchCamera(CameraEnum.UF); yield return UIManager.Instance.GetPanel().FadeInAsync(0.5f); ufSystem.OpenUFView(); @@ -65,9 +63,7 @@ namespace AibisDream.FixSystem var eyeModule = bodyModuleSystem.FindBodyModuleByType(); yield return eyeModule.OpenEye(); - // 插头回到初始插孔 - FixPanelSystem.GetRepairPanel().ResetPlug(); - // 切换相机 + FixPanelSystem.GetRepairPanel()?.PullUpCable(); yield return CameraKit.Instance.SwitchCamera(CameraEnum.Eye); yield return UIManager.Instance.GetPanel().FadeInAsync(0.5f); yield return CameraKit.Instance.SwitchCamera(CameraEnum.EyeDeep); @@ -118,9 +114,7 @@ namespace AibisDream.FixSystem } yield return memorySystem.DropMemoryProjector(); - // 插头回到初始插孔 - FixPanelSystem.GetRepairPanel().ResetPlug(); - // 相机聚焦到目标位 + FixPanelSystem.GetRepairPanel()?.PullUpCable(); yield return CameraKit.Instance.SwitchCamera(CameraEnum.Memory); yield return UIManager.Instance.GetPanel().FadeInAsync(duration); memorySystem.OnEnter(); @@ -134,7 +128,7 @@ namespace AibisDream.FixSystem FixPanelSystem.Instance.TaskPanel?.CollapsePanelTemporary(immediate: true); yield return memorySystem.DropMemoryProjector(); - FixPanelSystem.GetRepairPanel().ResetPlug(); + FixPanelSystem.GetRepairPanel()?.PullUpCable(); yield return CameraKit.Instance.SwitchCamera(CameraEnum.Memory, true); memorySystem.OnEnter(); BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Memory);