feat(scene-mgmt): 重构梦境门海浪切换演出

This commit is contained in:
2026-07-19 20:40:20 +08:00
parent bfbfe494c4
commit bfc61a0097
14 changed files with 1097 additions and 628 deletions
+366 -142
View File
@@ -4,7 +4,6 @@ using AibisDream.UI;
using AibisDream.Utility;
using DG.Tweening;
using UnityEngine;
using UnityEngine.Video;
namespace AibisDream
{
@@ -21,12 +20,23 @@ namespace AibisDream
[Tooltip("仅放门的静态 background,不要把 glassSea/codeSea/overlay 拖进来。")]
[SerializeField] private Renderer[] backgroundRenderers;
[SerializeField] private Renderer[] frameRenderers;
[SerializeField] private Sprite whiteDoorFrameSprite;
[SerializeField] private Sprite seaDoorFrameSprite;
[SerializeField] private SpriteRenderer sceneBlackOverlay;
[Header("Door Sea Memory")]
[SerializeField] private VideoClip glassSeaVideoClip;
[SerializeField] private RenderTexture glassSeaTargetTexture;
[SerializeField, Min(0.5f)] private float glassSeaPrepareTimeout = 5f;
[Header("Door Sea Animation")]
[SerializeField] private Animator glassSeaAnimator;
[SerializeField] private string glassSeaStateName = "DoorGlassSeaLoop";
[SerializeField] private string glassSeaFramedStateName = "DoorGlassSeaFramedLoop";
[SerializeField, Min(1f)] private float glassSeaCoverOverscan = 1.02f;
[Header("Code Sea Animation")]
[Tooltip("代码海切入时的固定波浪相位(单位:字符网格行)。用于匹配金色海浪切换前的构图。")]
[SerializeField] private float codeSeaStartPhase = 7.5f;
[Tooltip("代码海由远向近推进的速度(单位:字符网格行/秒)。")]
[SerializeField, Min(0f)] private float codeSeaWaveSpeed = 2.6f;
[Tooltip("0/1 和波浪符号独立于海浪移动的闪烁速度。")]
[SerializeField, Min(0f)] private float codeSeaGlyphSpeed = 3.2f;
[Header("Terminal(挂在 Door 子节点上即可,无需 Canvas")]
[SerializeField] private DreamTerminalUI terminalUI;
@@ -55,6 +65,7 @@ namespace AibisDream
[SerializeField] private string morphStartTimelineName = "姐姐/Glitch";
private Transform _codeSeaTransform;
private Vector3 _codeSeaOriginalLocalPosition;
private Vector3 _codeSeaOriginalScale;
private Color _codeSeaOriginalColor;
private Transform _terminalUiTransform;
@@ -62,15 +73,16 @@ namespace AibisDream
private Vector3 _terminalUiOriginalLocalPosition;
private Transform _doorShellTransform;
private Vector3 _doorShellOriginalScale;
private VideoPlayer _glassSeaVideoPlayer;
private Material _glassSeaMaterial;
private bool _glassSeaVideoPrepared;
private bool _glassSeaVideoFailed;
private bool _glassSeaFrameReady;
private Vector3 _glassSeaOriginalLocalPosition;
private Vector3 _glassSeaOriginalLocalScale;
private string _glassSeaCurrentStateName;
private MaterialPropertyBlock _codeSeaPropertyBlock;
private bool _codeSeaPlaybackActive;
private static readonly int WaveStartTimeId = Shader.PropertyToID("_WaveStartTime");
private static readonly int WavePhaseId = Shader.PropertyToID("_WavePhase");
private static readonly int WaveSpeedId = Shader.PropertyToID("_WaveSpeed");
private static readonly int GlyphSpeedId = Shader.PropertyToID("_GlyphSpeed");
private DoorPresentationPhase _presentationPhase = DoorPresentationPhase.Hidden;
private static readonly int UseVideoId = Shader.PropertyToID("_UseVideo");
private enum FrameHidePhase
{
Never,
@@ -98,21 +110,26 @@ namespace AibisDream
if (glassSea != null)
{
_glassSeaMaterial = glassSea.material;
SetGlassSeaVideoVisible(false);
_glassSeaOriginalLocalPosition = glassSea.transform.localPosition;
_glassSeaOriginalLocalScale = glassSea.transform.localScale;
if (glassSeaAnimator == null)
glassSeaAnimator = glassSea.GetComponent<Animator>();
glassSea.SetAlpha(0);
StopGlassSeaAnimation(resetToFirstFrame: true);
glassSea.gameObject.SetActive(false);
}
InitializeGlassSeaVideo();
SetDoorFrameSprite(hasSea: false);
if (codeSea != null)
{
codeSea.SetAlpha(0);
codeSea.gameObject.SetActive(false);
_codeSeaTransform = codeSea.transform;
_codeSeaOriginalLocalPosition = _codeSeaTransform.localPosition;
_codeSeaOriginalScale = _codeSeaTransform.localScale;
_codeSeaOriginalColor = codeSea.color;
codeSea.SetAlpha(0);
codeSea.gameObject.SetActive(false);
}
if (flashOverlay != null)
@@ -139,26 +156,14 @@ namespace AibisDream
_presentationPhase = DoorPresentationPhase.Hidden;
}
public override void OnSingletonDestroy()
{
if (_glassSeaVideoPlayer != null)
{
_glassSeaVideoPlayer.prepareCompleted -= OnGlassSeaVideoPrepared;
_glassSeaVideoPlayer.frameReady -= OnGlassSeaVideoFrameReady;
_glassSeaVideoPlayer.errorReceived -= OnGlassSeaVideoError;
}
base.OnSingletonDestroy();
}
/// <summary>
/// 显示普通门。该命令是一个完整状态切换,会清掉之前残留的海景、代码海浪和终端。
/// </summary>
public IEnumerator ShowDoor(float duration = 0f)
{
StopAllEffects();
EnsureDoorBaseVisible();
ResetGlassSeaImmediate(stopVideo: true);
EnsureDoorBaseVisible(hasSea: false);
ResetGlassSeaImmediate();
ResetCodeSeaImmediate();
ResetTerminalImmediate();
_presentationPhase = DoorPresentationPhase.Door;
@@ -167,6 +172,36 @@ namespace AibisDream
yield return new WaitForSeconds(duration);
}
/// <summary>
/// 进入“仔细听”的门近景。首次是白底门,之后直接显示门后的金色海浪。
/// </summary>
public IEnumerator ShowListenView(bool showSea, float duration = 0f)
{
StopAllEffects();
EnsureDoorBaseVisible(showSea);
ResetCodeSeaImmediate();
ResetTerminalImmediate();
var showcase = SpriteShowcase.Instance;
if (showcase != null)
showcase.SetLargePresentationImmediate(1f, 1f);
if (showSea && glassSea != null)
{
glassSea.SetAlpha(1f);
StartGlassSeaAnimation(glassSeaFramedStateName);
_presentationPhase = DoorPresentationPhase.GlassSea;
}
else
{
ResetGlassSeaImmediate();
_presentationPhase = DoorPresentationPhase.Door;
}
if (duration > 0f)
yield return new WaitForSeconds(duration);
}
/// <summary>
/// 隐藏整个门
/// </summary>
@@ -184,16 +219,16 @@ namespace AibisDream
yield break;
StopAllEffects();
EnsureDoorBaseVisible();
EnsureDoorBaseVisible(hasSea: true);
ResetCodeSeaImmediate();
ResetTerminalImmediate();
if (_presentationPhase != DoorPresentationPhase.GlassSea || !glassSea.gameObject.activeSelf)
glassSea.SetAlpha(0f);
glassSea.gameObject.SetActive(true);
StartGlassSeaAnimation(glassSeaFramedStateName);
_presentationPhase = DoorPresentationPhase.GlassSea;
yield return EnsureGlassSeaVideoPlaying();
yield return glassSea.DOFade(1f, Mathf.Max(0f, duration))
.SetEase(Ease.InOutSine)
.WaitForCompletion();
@@ -203,7 +238,6 @@ namespace AibisDream
{
if (glassSea == null)
{
PauseGlassSeaVideo();
yield break;
}
@@ -217,13 +251,92 @@ namespace AibisDream
}
glassSea.SetAlpha(0f);
StopGlassSeaAnimation(resetToFirstFrame: true);
glassSea.gameObject.SetActive(false);
PauseGlassSeaVideo();
if (_presentationPhase == DoorPresentationPhase.GlassSea)
_presentationPhase = DoorPresentationPhase.Door;
}
/// <summary>
/// 只调整近景门图片的缩放与透明度,不包含白幕、Bloom 或海浪状态切换。
/// </summary>
public IEnumerator TweenListenCloseup(float scale, float alpha, float duration)
{
var showcase = SpriteShowcase.Instance;
if (showcase == null)
yield break;
yield return showcase.AnimateLargePresentation(scale, alpha, duration);
}
public void SetListenCloseup(float scale, float alpha)
{
SpriteShowcase.Instance?.SetLargePresentationImmediate(scale, alpha);
}
/// <summary>
/// 全白遮罩下调用的瞬时状态切换:隐藏门底和门框,仅显示全屏金色海浪。
/// </summary>
public void ShowGlassSeaFullscreen()
{
if (doorRoot != null)
doorRoot.gameObject.SetActive(true);
SetBackgroundVisible(false);
SetFrameVisible(false);
ResetCodeSeaImmediate();
EnsureGlassSeaVisible(glassSeaStateName);
}
/// <summary>
/// 全白遮罩下调用的瞬时状态切换:恢复门底和门框,金色海浪留在门后。
/// </summary>
public void ShowGlassSeaFramed()
{
ResetCodeSeaImmediate();
EnsureDoorBaseVisible(hasSea: true);
EnsureGlassSeaVisible(glassSeaFramedStateName);
}
/// <summary>
/// 全白时切到无门的全屏 CodeSea;状态职责与 ShowGlassSeaFullscreen 一致。
/// </summary>
public void ShowCodeSeaFullscreen()
{
if (doorRoot != null)
doorRoot.gameObject.SetActive(true);
var restartPlayback = _presentationPhase != DoorPresentationPhase.CodeSea
|| !_codeSeaPlaybackActive;
SetBackgroundVisible(false);
SetFrameVisible(false);
ResetGlassSeaImmediate();
EnsureCodeSeaVisible(restartPlayback);
}
/// <summary>
/// 全白时恢复门框,CodeSea 保持显示在门内。
/// </summary>
public void ShowCodeSeaFramed()
{
var restartPlayback = _presentationPhase != DoorPresentationPhase.CodeSea
|| !_codeSeaPlaybackActive;
ResetGlassSeaImmediate();
EnsureDoorBaseVisible(hasSea: false);
EnsureCodeSeaFramedVisible(restartPlayback);
}
private void ResetListenTransitionImmediate()
{
var panel = UIManager.Instance != null
? UIManager.Instance.GetPanel<ScreenTransitionPanel>()
: null;
panel?.FadeOutSync(0f);
SpriteShowcase.Instance?.ResetLargePresentationImmediate();
}
#endregion
#region Phase 2: CodeSea (GlassSea )
@@ -233,15 +346,21 @@ namespace AibisDream
if (codeSea == null)
yield break;
var restartPlayback = _presentationPhase != DoorPresentationPhase.CodeSea
|| !_codeSeaPlaybackActive;
StopAllEffects();
EnsureDoorBaseVisible();
EnsureDoorBaseVisible(hasSea: false);
ResetTerminalImmediate();
codeSea.color = _codeSeaOriginalColor;
if (_codeSeaTransform != null)
{
_codeSeaTransform.localPosition = _codeSeaOriginalLocalPosition;
_codeSeaTransform.localScale = _codeSeaOriginalScale;
}
codeSea.SetAlpha(0f);
codeSea.gameObject.SetActive(true);
BeginCodeSeaPlayback(restartPlayback);
_presentationPhase = DoorPresentationPhase.CodeSea;
if (frameHidePhase == FrameHidePhase.OnCodeSea)
@@ -259,10 +378,10 @@ namespace AibisDream
if (glassSea != null)
{
glassSea.SetAlpha(0f);
StopGlassSeaAnimation(resetToFirstFrame: true);
glassSea.gameObject.SetActive(false);
}
StopGlassSeaVideo();
}
#endregion
@@ -302,12 +421,16 @@ namespace AibisDream
SetFrameVisible(false);
if (glassSea != null)
{
StopGlassSeaAnimation(resetToFirstFrame: true);
glassSea.gameObject.SetActive(false);
StopGlassSeaVideo();
}
if (codeSea != null)
{
codeSea.gameObject.SetActive(false);
ResetCodeSeaPlayback();
}
if (flashOverlay != null)
flashOverlay.SetAlpha(0f);
@@ -387,6 +510,7 @@ namespace AibisDream
DOTween.Kill(_doorShellTransform);
DOTween.Kill(flashOverlay);
DOTween.Kill(sceneBlackOverlay);
SpriteShowcase.Instance?.StopLargePresentationAnimation();
}
#endregion
@@ -396,19 +520,20 @@ namespace AibisDream
public void HideAll()
{
StopAllEffects();
ResetListenTransitionImmediate();
HideDoor();
SetBackgroundVisible(true);
SetFrameVisible(true);
SetDoorFrameSprite(hasSea: false);
ResetDoorShellScale();
if (glassSea != null)
{
glassSea.SetAlpha(0);
StopGlassSeaAnimation(resetToFirstFrame: true);
glassSea.gameObject.SetActive(false);
}
StopGlassSeaVideo();
if (codeSea != null)
{
codeSea.color = _codeSeaOriginalColor;
@@ -417,6 +542,7 @@ namespace AibisDream
if (_codeSeaTransform != null)
_codeSeaTransform.localScale = _codeSeaOriginalScale;
}
ResetCodeSeaPlayback();
if (terminalUI != null)
{
@@ -436,6 +562,12 @@ namespace AibisDream
if (sceneBlackOverlay != null)
sceneBlackOverlay.SetAlpha(0);
var showcase = SpriteShowcase.Instance;
if (showcase != null)
{
showcase.HideLargePresentationImmediate();
}
_presentationPhase = DoorPresentationPhase.Hidden;
}
@@ -443,30 +575,44 @@ namespace AibisDream
#region Presentation State
private void EnsureDoorBaseVisible()
private void EnsureDoorBaseVisible(bool hasSea = false)
{
if (doorRoot != null)
doorRoot.gameObject.SetActive(true);
SetBackgroundVisible(true);
SetFrameVisible(true);
SetDoorFrameSprite(hasSea);
ResetDoorShellScale();
}
private void ResetGlassSeaImmediate(bool stopVideo)
private void ResetGlassSeaImmediate()
{
DOTween.Kill(glassSea);
if (glassSea != null)
{
glassSea.SetAlpha(0f);
StopGlassSeaAnimation(resetToFirstFrame: true);
glassSea.gameObject.SetActive(false);
}
}
if (stopVideo)
StopGlassSeaVideo();
private void EnsureGlassSeaVisible(string stateName)
{
if (glassSea == null)
return;
glassSea.SetAlpha(1f);
if (!glassSea.gameObject.activeSelf
|| glassSeaAnimator == null
|| !glassSeaAnimator.enabled
|| _glassSeaCurrentStateName != stateName)
StartGlassSeaAnimation(stateName);
else
PauseGlassSeaVideo();
FitGlassSeaToCameraCover();
_presentationPhase = DoorPresentationPhase.GlassSea;
}
private void ResetCodeSeaImmediate()
@@ -474,6 +620,8 @@ namespace AibisDream
DOTween.Kill(_codeSeaTransform);
DOTween.Kill(codeSea);
ResetCodeSeaPlayback();
if (codeSea == null)
return;
@@ -482,7 +630,62 @@ namespace AibisDream
codeSea.gameObject.SetActive(false);
if (_codeSeaTransform != null)
{
_codeSeaTransform.localPosition = _codeSeaOriginalLocalPosition;
_codeSeaTransform.localScale = _codeSeaOriginalScale;
}
}
private void EnsureCodeSeaVisible(bool restartPlayback)
{
if (codeSea == null)
return;
codeSea.color = _codeSeaOriginalColor;
codeSea.SetAlpha(1f);
codeSea.gameObject.SetActive(true);
BeginCodeSeaPlayback(restartPlayback);
FitCodeSeaToCameraCover();
_presentationPhase = DoorPresentationPhase.CodeSea;
}
private void EnsureCodeSeaFramedVisible(bool restartPlayback)
{
if (codeSea == null)
return;
codeSea.color = _codeSeaOriginalColor;
codeSea.SetAlpha(1f);
codeSea.gameObject.SetActive(true);
BeginCodeSeaPlayback(restartPlayback);
if (_codeSeaTransform != null)
{
_codeSeaTransform.localPosition = _codeSeaOriginalLocalPosition;
_codeSeaTransform.localScale = _codeSeaOriginalScale;
}
_presentationPhase = DoorPresentationPhase.CodeSea;
}
private void BeginCodeSeaPlayback(bool restartPlayback)
{
if (codeSea == null || (_codeSeaPlaybackActive && !restartPlayback))
return;
_codeSeaPropertyBlock ??= new MaterialPropertyBlock();
codeSea.GetPropertyBlock(_codeSeaPropertyBlock);
_codeSeaPropertyBlock.SetFloat(WaveStartTimeId, Time.time);
_codeSeaPropertyBlock.SetFloat(WavePhaseId, codeSeaStartPhase);
_codeSeaPropertyBlock.SetFloat(WaveSpeedId, codeSeaWaveSpeed);
_codeSeaPropertyBlock.SetFloat(GlyphSpeedId, codeSeaGlyphSpeed);
codeSea.SetPropertyBlock(_codeSeaPropertyBlock);
_codeSeaPlaybackActive = true;
}
private void ResetCodeSeaPlayback()
{
_codeSeaPlaybackActive = false;
}
private void ResetTerminalImmediate()
@@ -510,118 +713,121 @@ namespace AibisDream
#endregion
#region GlassSea Video
#region GlassSea Animation
private void InitializeGlassSeaVideo()
private void StartGlassSeaAnimation(string stateName = null)
{
if (glassSeaVideoClip == null || glassSeaTargetTexture == null)
if (glassSea == null) return;
stateName = string.IsNullOrEmpty(stateName) ? glassSeaStateName : stateName;
glassSea.gameObject.SetActive(true);
if (glassSeaAnimator != null && glassSeaAnimator.runtimeAnimatorController != null)
{
SetGlassSeaVideoVisible(false);
return;
glassSeaAnimator.enabled = true;
glassSeaAnimator.Play(stateName, 0, 0f);
glassSeaAnimator.Update(0f);
_glassSeaCurrentStateName = stateName;
}
_glassSeaVideoPlayer = GetComponent<VideoPlayer>();
if (_glassSeaVideoPlayer == null)
_glassSeaVideoPlayer = gameObject.AddComponent<VideoPlayer>();
_glassSeaVideoPlayer.playOnAwake = false;
_glassSeaVideoPlayer.source = VideoSource.VideoClip;
_glassSeaVideoPlayer.clip = glassSeaVideoClip;
_glassSeaVideoPlayer.renderMode = VideoRenderMode.RenderTexture;
_glassSeaVideoPlayer.targetTexture = glassSeaTargetTexture;
_glassSeaVideoPlayer.audioOutputMode = VideoAudioOutputMode.None;
_glassSeaVideoPlayer.isLooping = true;
_glassSeaVideoPlayer.skipOnDrop = true;
_glassSeaVideoPlayer.waitForFirstFrame = true;
_glassSeaVideoPlayer.sendFrameReadyEvents = true;
_glassSeaVideoPlayer.prepareCompleted += OnGlassSeaVideoPrepared;
_glassSeaVideoPlayer.frameReady += OnGlassSeaVideoFrameReady;
_glassSeaVideoPlayer.errorReceived += OnGlassSeaVideoError;
_glassSeaVideoPrepared = false;
_glassSeaVideoFailed = false;
_glassSeaFrameReady = false;
SetGlassSeaVideoVisible(false);
_glassSeaVideoPlayer.Prepare();
FitGlassSeaToCameraCover();
}
private IEnumerator EnsureGlassSeaVideoPlaying()
private void StopGlassSeaAnimation(bool resetToFirstFrame)
{
if (_glassSeaVideoPlayer == null || _glassSeaVideoFailed)
if (glassSeaAnimator == null) return;
if (resetToFirstFrame
&& glassSeaAnimator.runtimeAnimatorController != null
&& glassSeaAnimator.gameObject.activeInHierarchy)
{
SetGlassSeaVideoVisible(false);
yield break;
var stateName = string.IsNullOrEmpty(_glassSeaCurrentStateName)
? glassSeaStateName
: _glassSeaCurrentStateName;
glassSeaAnimator.enabled = true;
glassSeaAnimator.Play(stateName, 0, 0f);
glassSeaAnimator.Update(0f);
}
if (!_glassSeaVideoPrepared && !_glassSeaVideoPlayer.isPrepared)
{
float deadline = Time.realtimeSinceStartup + glassSeaPrepareTimeout;
while (!_glassSeaVideoPrepared && !_glassSeaVideoFailed && Time.realtimeSinceStartup < deadline)
yield return null;
glassSeaAnimator.enabled = false;
_glassSeaCurrentStateName = null;
}
if (!_glassSeaVideoPrepared && !_glassSeaVideoPlayer.isPrepared)
{
_glassSeaVideoFailed = true;
_glassSeaVideoPlayer.Stop();
SetGlassSeaVideoVisible(false);
Debug.LogWarning("[DreamDoorSystem] 海景视频准备超时,回退到静态海报。", this);
yield break;
}
private void FitGlassSeaToCameraCover()
{
FitSpriteToCameraCover(
glassSea,
_glassSeaOriginalLocalPosition,
_glassSeaOriginalLocalScale);
}
private void FitCodeSeaToCameraCover()
{
FitSpriteToCameraCover(
codeSea,
_codeSeaOriginalLocalPosition,
_codeSeaOriginalScale);
}
private void FitSpriteToCameraCover(
SpriteRenderer target,
Vector3 originalLocalPosition,
Vector3 originalLocalScale)
{
if (target == null || target.sprite == null) return;
var targetCamera = ResolveDoorCamera();
if (targetCamera == null) return;
var seaTransform = target.transform;
var parent = seaTransform.parent;
var depth = Vector3.Dot(seaTransform.position - targetCamera.transform.position, targetCamera.transform.forward);
depth = Mathf.Max(depth, targetCamera.nearClipPlane + 0.05f);
float viewWidth;
float viewHeight;
if (targetCamera.orthographic)
{
viewHeight = targetCamera.orthographicSize * 2f;
viewWidth = viewHeight * targetCamera.aspect;
}
else
{
var bottomLeft = targetCamera.ViewportToWorldPoint(new Vector3(0f, 0f, depth));
var bottomRight = targetCamera.ViewportToWorldPoint(new Vector3(1f, 0f, depth));
var topLeft = targetCamera.ViewportToWorldPoint(new Vector3(0f, 1f, depth));
viewWidth = Vector3.Distance(bottomLeft, bottomRight);
viewHeight = Vector3.Distance(bottomLeft, topLeft);
}
_glassSeaVideoPrepared = true;
if (!_glassSeaVideoPlayer.isPlaying)
_glassSeaVideoPlayer.Play();
var spriteSize = target.sprite.bounds.size;
if (spriteSize.x <= 0.001f || spriteSize.y <= 0.001f) return;
if (!_glassSeaFrameReady)
var parentScale = parent != null ? parent.lossyScale : Vector3.one;
var localScale = Mathf.Max(
viewWidth / (spriteSize.x * Mathf.Max(Mathf.Abs(parentScale.x), 0.001f)),
viewHeight / (spriteSize.y * Mathf.Max(Mathf.Abs(parentScale.y), 0.001f)));
localScale *= glassSeaCoverOverscan;
var worldCenter = targetCamera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, depth));
var localCenter = parent != null ? parent.InverseTransformPoint(worldCenter) : worldCenter;
localCenter.z = originalLocalPosition.z;
seaTransform.localPosition = localCenter;
seaTransform.localScale = new Vector3(localScale, localScale, originalLocalScale.z);
}
private Camera ResolveDoorCamera()
{
foreach (var candidate in Camera.allCameras)
{
float firstFrameDeadline = Time.realtimeSinceStartup + 1f;
while (!_glassSeaFrameReady && !_glassSeaVideoFailed && Time.realtimeSinceStartup < firstFrameDeadline)
yield return null;
if (candidate != null
&& candidate.isActiveAndEnabled
&& candidate.gameObject.scene == gameObject.scene)
return candidate;
}
}
private void PauseGlassSeaVideo()
{
if (_glassSeaVideoPlayer != null && _glassSeaVideoPlayer.isPlaying)
_glassSeaVideoPlayer.Pause();
}
private void StopGlassSeaVideo()
{
if (_glassSeaVideoPlayer != null)
_glassSeaVideoPlayer.Stop();
_glassSeaFrameReady = false;
SetGlassSeaVideoVisible(false);
}
private void OnGlassSeaVideoPrepared(VideoPlayer source)
{
_glassSeaVideoPrepared = true;
_glassSeaVideoFailed = false;
}
private void OnGlassSeaVideoFrameReady(VideoPlayer source, long frameIndex)
{
_glassSeaFrameReady = true;
SetGlassSeaVideoVisible(true);
}
private void OnGlassSeaVideoError(VideoPlayer source, string message)
{
_glassSeaVideoFailed = true;
_glassSeaVideoPrepared = false;
_glassSeaFrameReady = false;
SetGlassSeaVideoVisible(false);
Debug.LogWarning($"[DreamDoorSystem] 海景视频播放失败,回退到静态海报:{message}", this);
}
private void SetGlassSeaVideoVisible(bool visible)
{
if (_glassSeaMaterial != null && _glassSeaMaterial.HasProperty(UseVideoId))
_glassSeaMaterial.SetFloat(UseVideoId, visible ? 1f : 0f);
return Camera.main;
}
#endregion
@@ -637,6 +843,24 @@ namespace AibisDream
}
}
private void SetDoorFrameSprite(bool hasSea)
{
var targetSprite = hasSea ? seaDoorFrameSprite : whiteDoorFrameSprite;
if (targetSprite == null)
return;
if (frameRenderers != null)
{
foreach (var frameRenderer in frameRenderers)
{
if (frameRenderer is SpriteRenderer spriteRenderer)
spriteRenderer.sprite = targetSprite;
}
}
SpriteShowcase.Instance?.SetLargeSpriteImmediate(targetSprite);
}
private void SetBackgroundVisible(bool visible)
{
if (backgroundRenderers == null) return;