using System.Collections; using AibisDream.Kit; using AibisDream.UI; using AibisDream.Utility; using DG.Tweening; using UnityEngine; namespace AibisDream { public class DreamDoorSystem : Singleton { [Header("Door Root")] [Tooltip("Door 根节点。默认仅 background 和 Frame 显示,整个 Door 初始隐藏。show_door 命令激活后显示。")] [SerializeField] private Transform doorRoot; [Header("Door Sprites")] [SerializeField] private SpriteRenderer glassSea; [SerializeField] private SpriteRenderer codeSea; [SerializeField] private SpriteRenderer flashOverlay; [Header("Terminal(挂在 Door 子节点上即可,无需 Canvas)")] [SerializeField] private DreamTerminalUI terminalUI; [Header("Morph Settings")] [SerializeField] private Vector3 seaCollapseScale = new(0.9f, 0.84f, 1f); [SerializeField] private Ease morphEase = Ease.InOutCubic; private Transform _codeSeaTransform; private Vector3 _codeSeaOriginalScale; private Color _codeSeaOriginalColor; private Transform _terminalUiTransform; private Vector3 _terminalUiOriginalScale; public override void OnSingletonInit() { if (doorRoot != null) doorRoot.gameObject.SetActive(false); if (glassSea != null) { glassSea.SetAlpha(0); glassSea.gameObject.SetActive(false); } if (codeSea != null) { codeSea.SetAlpha(0); codeSea.gameObject.SetActive(false); _codeSeaTransform = codeSea.transform; _codeSeaOriginalScale = _codeSeaTransform.localScale; _codeSeaOriginalColor = codeSea.color; } if (flashOverlay != null) { flashOverlay.SetAlpha(0); flashOverlay.gameObject.SetActive(true); } if (terminalUI != null) { terminalUI.ResetVisualState(); terminalUI.Alpha = 0f; _terminalUiTransform = terminalUI.transform; _terminalUiOriginalScale = _terminalUiTransform.localScale; } } /// /// 显示整个门(仅 background + Frame,GlassSea/CodeSea 保持隐藏直到各自命令调用) /// public IEnumerator ShowDoor(float duration = 0f) { if (doorRoot != null) doorRoot.gameObject.SetActive(true); if (duration > 0f) yield return new WaitForSeconds(duration); } /// /// 隐藏整个门 /// public void HideDoor() { if (doorRoot != null) doorRoot.gameObject.SetActive(false); } #region Phase 1: GlassSea 浮现 public IEnumerator ShowGlassSea(float duration = 2f) { glassSea.gameObject.SetActive(true); yield return glassSea.DOFade(1f, duration) .SetEase(Ease.InOutSine) .WaitForCompletion(); } public IEnumerator HideGlassSea(float duration = 1f) { yield return glassSea.DOFade(0f, duration) .SetEase(Ease.InOutSine) .WaitForCompletion(); glassSea.gameObject.SetActive(false); } #endregion #region Phase 2: CodeSea 浮现 (GlassSea 交叉淡出) public IEnumerator ShowCodeSea(float duration = 2f) { codeSea.gameObject.SetActive(true); var seq = DOTween.Sequence(); seq.Append(codeSea.DOFade(1f, duration).SetEase(Ease.InOutSine)); if (glassSea != null && glassSea.gameObject.activeSelf) { seq.Join(glassSea.DOFade(0f, duration * 0.7f).SetEase(Ease.InSine)); } yield return seq.WaitForCompletion(); if (glassSea != null) glassSea.gameObject.SetActive(false); } #endregion #region Phase 3: CodeSea 变形为终端 public IEnumerator ShowTerminal() { yield return AnimateDoorToTerminal(); if (terminalUI != null) terminalUI.Begin(); } private IEnumerator AnimateDoorToTerminal() { if (terminalUI != null) { terminalUI.ResetVisualState(); terminalUI.Alpha = 0f; terminalUI.MorphProgress = 0f; } if (_terminalUiTransform != null) _terminalUiTransform.localScale = Vector3.Scale(_terminalUiOriginalScale, new Vector3(1.04f, 1.04f, 1f)); var seq = DOTween.Sequence(); // Step 1: 短暂闪白(CRT 切换感) if (flashOverlay != null) { seq.Append(flashOverlay.DOFade(0.8f, 0.05f)); seq.Append(flashOverlay.DOFade(0f, 0.12f)); } // Step 2: 海浪内容退场,终端框架在门内成型 if (codeSea != null) { codeSea.gameObject.SetActive(true); seq.Append(codeSea.DOColor(new Color(0.04f, 0.04f, 0.04f, 1f), 0.3f) .SetEase(Ease.OutSine)); seq.Join(codeSea.DOFade(0f, 0.78f).SetEase(Ease.InSine)); } if (_codeSeaTransform != null) { seq.Join(_codeSeaTransform.DOScale(Vector3.Scale(_codeSeaOriginalScale, seaCollapseScale), 0.78f) .SetEase(morphEase)); } if (terminalUI != null) { float uiAlpha = 0f; float morph = 0f; seq.Join(DOTween.To(() => uiAlpha, v => { uiAlpha = v; terminalUI.Alpha = v; }, 1f, 0.24f).SetEase(Ease.OutQuad)); seq.Join(DOTween.To(() => morph, v => { morph = v; terminalUI.MorphProgress = v; }, 1f, 0.82f).SetEase(morphEase)); } if (_terminalUiTransform != null) { seq.Join(_terminalUiTransform.DOScale(_terminalUiOriginalScale, 0.82f) .SetEase(Ease.OutCubic)); } // Step 3: 第二次微闪(终端稳定) if (flashOverlay != null) { seq.Append(flashOverlay.DOFade(0.5f, 0.03f)); seq.Append(flashOverlay.DOFade(0f, 0.15f)); } yield return seq.WaitForCompletion(); if (codeSea != null) codeSea.gameObject.SetActive(false); } #endregion #region 终端结局动效 public IEnumerator PlayShutdownEffect() { var target = _terminalUiTransform != null ? _terminalUiTransform : _codeSeaTransform; if (target == null) yield break; var seq = DOTween.Sequence(); if (flashOverlay != null) seq.Append(flashOverlay.DOFade(0.6f, 0.15f)); // 压扁成白线 float scaleY = _terminalUiTransform != null ? _terminalUiOriginalScale.y * 0.02f : 0.005f; seq.Join(target.DOScaleY(scaleY, 0.35f).SetEase(Ease.OutQuad)); // 横向收缩消失 seq.Append(target.DOScaleX(0f, 0.3f).SetEase(Ease.InQuad)); if (flashOverlay != null) seq.Join(flashOverlay.DOFade(0f, 0.5f)); // 同步淡出 Shapes 终端 if (terminalUI != null) { float a = terminalUI.Alpha; seq.Join(DOTween.To(() => a, v => { a = v; terminalUI.Alpha = v; }, 0f, 0.4f)); } yield return seq.WaitForCompletion(); } public void PlayCrashEffect() { var target = _terminalUiTransform != null ? _terminalUiTransform : _codeSeaTransform; if (target != null) target.DOShakePosition(1.2f, 0.08f, 35, fadeOut: false); } public void StopAllEffects() { DOTween.Kill(_codeSeaTransform); DOTween.Kill(codeSea); DOTween.Kill(_terminalUiTransform); DOTween.Kill(flashOverlay); } #endregion #region 清理 public void HideAll() { StopAllEffects(); HideDoor(); if (glassSea != null) { glassSea.SetAlpha(0); glassSea.gameObject.SetActive(false); } if (codeSea != null) { codeSea.color = _codeSeaOriginalColor; codeSea.SetAlpha(0); codeSea.gameObject.SetActive(false); if (_codeSeaTransform != null) _codeSeaTransform.localScale = _codeSeaOriginalScale; } if (terminalUI != null) { terminalUI.Alpha = 0f; terminalUI.ResetVisualState(); } if (_terminalUiTransform != null) _terminalUiTransform.localScale = _terminalUiOriginalScale; if (flashOverlay != null) flashOverlay.SetAlpha(0); } #endregion #if UNITY_EDITOR private void OnDrawGizmosSelected() { if (codeSea == null) return; Gizmos.color = new Color(1f, 0.7f, 0f, 0.4f); var bounds = codeSea.bounds; Gizmos.DrawWireCube(bounds.center, bounds.size); Gizmos.color = new Color(0f, 1f, 1f, 0.3f); Vector3 termSize = new( bounds.size.x * seaCollapseScale.x, bounds.size.y * seaCollapseScale.y, bounds.size.z ); Gizmos.DrawWireCube(bounds.center, termSize); } #endif } }