322 lines
11 KiB
C#
322 lines
11 KiB
C#
using System;
|
|
using System.Collections;
|
|
using AibisDream.Framework;
|
|
using AibisDream.Kit;
|
|
using AibisDream.SaveSystem;
|
|
using AibisDream.Utility;
|
|
using DG.Tweening;
|
|
using UnityEngine;
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
|
|
namespace AibisDream
|
|
{
|
|
/// <summary>
|
|
/// 梦境场景通用图片展示器。只负责普通小图、大图与背景遮罩;
|
|
/// 场景专属动态表现通过 <see cref="PresentationChanging"/> 与本组件解耦。
|
|
/// </summary>
|
|
public class SpriteShowcase : Singleton<SpriteShowcase>
|
|
{
|
|
private const string DreamSpritePath = "Sprite/梦境/{0}.png";
|
|
|
|
[SerializeField] private SpriteRenderer _spriteRenderer;
|
|
[SerializeField] private SpriteRenderer _backgroundSpriteRenderer;
|
|
[SerializeField] private SpriteRenderer _largeSpriteRenderer;
|
|
|
|
private string _currentSmallPicName;
|
|
private string _currentLargePicName;
|
|
|
|
public event Action PresentationChanging;
|
|
|
|
private bool IsSmallShowing => _spriteRenderer != null && _spriteRenderer.gameObject.activeSelf;
|
|
private bool IsLargeShowing => _largeSpriteRenderer != null && _largeSpriteRenderer.gameObject.activeSelf;
|
|
|
|
/// <summary>
|
|
/// 立即隐藏全屏遮罩(黑底)。门近景透明区域需要透出 Door HDR 白底时调用。
|
|
/// </summary>
|
|
public void HideBackgroundImmediate()
|
|
{
|
|
SetBackgroundImmediate(false);
|
|
}
|
|
|
|
public override void OnSingletonInit()
|
|
{
|
|
ClearSmallImmediate();
|
|
ClearLargeImmediate();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
ClearSmallImmediate();
|
|
ClearLargeImmediate();
|
|
}
|
|
|
|
private static string NormalizeDreamPicName(string picName)
|
|
{
|
|
if (string.IsNullOrEmpty(picName)) return picName;
|
|
return picName.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
|
? picName.Substring(0, picName.Length - 4)
|
|
: picName;
|
|
}
|
|
|
|
private IEnumerator LoadDreamSpriteInto(
|
|
SpriteRenderer target,
|
|
string picName,
|
|
Action<bool> completed = null,
|
|
Action<string> warning = null)
|
|
{
|
|
if (target == null)
|
|
{
|
|
completed?.Invoke(false);
|
|
yield break;
|
|
}
|
|
|
|
var key = string.Format(DreamSpritePath, NormalizeDreamPicName(picName));
|
|
var handle = ResourceSystem.LoadAsync<Sprite>(key);
|
|
yield return handle;
|
|
if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
|
|
{
|
|
target.sprite = handle.Result;
|
|
completed?.Invoke(true);
|
|
}
|
|
else
|
|
{
|
|
var message = $"[SpriteShowcase] Failed to load sprite: {key}";
|
|
Debug.LogError(message, this);
|
|
warning?.Invoke(message);
|
|
completed?.Invoke(false);
|
|
}
|
|
}
|
|
|
|
public IEnumerator ShowObj(string picName, float duration = 1f)
|
|
{
|
|
NotifyPresentationChanging();
|
|
|
|
if (IsLargeShowing)
|
|
yield return HideLargeInternal(duration);
|
|
|
|
if (_spriteRenderer == null) yield break;
|
|
_spriteRenderer.DOKill();
|
|
_spriteRenderer.gameObject.SetActive(true);
|
|
if (!CommonUtil.IsZero(duration)) _spriteRenderer.SetAlpha(0f);
|
|
|
|
var loaded = false;
|
|
yield return LoadDreamSpriteInto(_spriteRenderer, picName, value => loaded = value);
|
|
if (!loaded)
|
|
{
|
|
ClearSmallImmediate();
|
|
yield break;
|
|
}
|
|
|
|
_currentSmallPicName = NormalizeDreamPicName(picName);
|
|
if (duration <= 0f)
|
|
_spriteRenderer.SetAlpha(1f);
|
|
else
|
|
yield return _spriteRenderer.DOFade(1f, duration).WaitForCompletion();
|
|
}
|
|
|
|
public IEnumerator ShowLargeObj(string picName, float duration = 1f)
|
|
{
|
|
NotifyPresentationChanging();
|
|
|
|
if (IsSmallShowing)
|
|
yield return HideSmallInternal(0f);
|
|
|
|
if (_largeSpriteRenderer == null) yield break;
|
|
_largeSpriteRenderer.DOKill();
|
|
_largeSpriteRenderer.gameObject.SetActive(true);
|
|
if (!CommonUtil.IsZero(duration)) _largeSpriteRenderer.SetAlpha(0f);
|
|
|
|
var loaded = false;
|
|
yield return LoadDreamSpriteInto(_largeSpriteRenderer, picName, value => loaded = value);
|
|
if (!loaded)
|
|
{
|
|
ClearLargeImmediate();
|
|
yield break;
|
|
}
|
|
|
|
// Force _MainTex rebind: assigning the same Sprite after SetPropertyBlock(null)
|
|
// can leave URP Sprite-Lit with a null MainTex (pure white).
|
|
var assigned = _largeSpriteRenderer.sprite;
|
|
_largeSpriteRenderer.sprite = null;
|
|
_largeSpriteRenderer.sprite = assigned;
|
|
|
|
// 门近景 closeup 可能把 large 留在 2x/4x;换图时强制回到 1x。
|
|
Day2SleepPresentationController.Instance?.ResetLargePresentationImmediate();
|
|
_largeSpriteRenderer.gameObject.SetActive(true);
|
|
|
|
_currentLargePicName = NormalizeDreamPicName(picName);
|
|
if (CommonUtil.IsZero(duration))
|
|
_largeSpriteRenderer.SetAlpha(1f);
|
|
else
|
|
yield return _largeSpriteRenderer.DOFade(1f, duration).WaitForCompletion();
|
|
}
|
|
|
|
public IEnumerator HideObj(float duration = 1f)
|
|
{
|
|
yield return HideObj(false, duration);
|
|
}
|
|
|
|
public IEnumerator HideObj(bool force, float duration = 1f)
|
|
{
|
|
NotifyPresentationChanging();
|
|
if (!force && !IsSmallShowing) yield break;
|
|
yield return HideSmallInternal(duration);
|
|
}
|
|
|
|
public IEnumerator HideLargeObj(float duration = 1f)
|
|
{
|
|
NotifyPresentationChanging();
|
|
if (!IsLargeShowing) yield break;
|
|
yield return HideLargeInternal(duration);
|
|
}
|
|
|
|
public IEnumerator HideAll(float duration = 1f)
|
|
{
|
|
NotifyPresentationChanging();
|
|
if (IsSmallShowing)
|
|
yield return HideSmallInternal(duration);
|
|
if (IsLargeShowing)
|
|
yield return HideLargeInternal(duration);
|
|
}
|
|
|
|
private IEnumerator HideSmallInternal(float duration)
|
|
{
|
|
if (_spriteRenderer == null) yield break;
|
|
_spriteRenderer.DOKill();
|
|
if (duration > 0f)
|
|
yield return _spriteRenderer.DOFade(0f, duration).WaitForCompletion();
|
|
ClearSmallImmediate();
|
|
}
|
|
|
|
private IEnumerator HideLargeInternal(float duration)
|
|
{
|
|
if (_largeSpriteRenderer == null) yield break;
|
|
_largeSpriteRenderer.DOKill();
|
|
if (duration > 0f)
|
|
yield return _largeSpriteRenderer.DOFade(0f, duration).WaitForCompletion();
|
|
ClearLargeImmediate();
|
|
}
|
|
|
|
public IEnumerator FadeInAsync(float duration = 1f)
|
|
{
|
|
if (_backgroundSpriteRenderer == null) yield break;
|
|
_backgroundSpriteRenderer.gameObject.SetActive(true);
|
|
if (duration <= 0f)
|
|
_backgroundSpriteRenderer.SetAlpha(1f);
|
|
else
|
|
yield return _backgroundSpriteRenderer.DOFade(1f, duration).WaitForCompletion();
|
|
}
|
|
|
|
public IEnumerator FadeOutAsync(float duration = 1f)
|
|
{
|
|
if (_backgroundSpriteRenderer == null) yield break;
|
|
_backgroundSpriteRenderer.gameObject.SetActive(true);
|
|
if (duration > 0f)
|
|
yield return _backgroundSpriteRenderer.DOFade(0f, duration).WaitForCompletion();
|
|
SetBackgroundImmediate(false);
|
|
}
|
|
|
|
public void FadeInSync(float duration = 1f)
|
|
{
|
|
StartCoroutine(FadeInAsync(duration));
|
|
}
|
|
|
|
public void FadeOutSync(float duration = 1f)
|
|
{
|
|
StartCoroutine(FadeOutAsync(duration));
|
|
}
|
|
|
|
public void ClearPresentationEffects()
|
|
{
|
|
NotifyPresentationChanging();
|
|
ClearSmallImmediate();
|
|
ClearLargeImmediate();
|
|
SetBackgroundImmediate(false);
|
|
}
|
|
|
|
public ShowcaseSnapshotDto CaptureSnapshot()
|
|
{
|
|
var mode = ShowcaseDisplayMode.None;
|
|
var picName = string.Empty;
|
|
if (IsSmallShowing && !string.IsNullOrEmpty(_currentSmallPicName))
|
|
{
|
|
mode = ShowcaseDisplayMode.Small;
|
|
picName = _currentSmallPicName;
|
|
}
|
|
else if (IsLargeShowing && !string.IsNullOrEmpty(_currentLargePicName))
|
|
{
|
|
mode = ShowcaseDisplayMode.Large;
|
|
picName = _currentLargePicName;
|
|
}
|
|
|
|
return new ShowcaseSnapshotDto
|
|
{
|
|
displayMode = mode.ToString(),
|
|
picName = picName,
|
|
isBackgroundVisible = _backgroundSpriteRenderer != null
|
|
&& _backgroundSpriteRenderer.gameObject.activeSelf
|
|
&& _backgroundSpriteRenderer.color.a > 0.001f
|
|
};
|
|
}
|
|
|
|
public IEnumerator RestoreSnapshotAsync(ShowcaseSnapshotDto snapshot, Action<string> warning = null)
|
|
{
|
|
ClearPresentationEffects();
|
|
if (snapshot == null) yield break;
|
|
|
|
if (!Enum.TryParse(snapshot.displayMode, true, out ShowcaseDisplayMode mode))
|
|
{
|
|
warning?.Invoke($"[SpriteShowcase] Unknown display mode: {snapshot.displayMode}");
|
|
mode = ShowcaseDisplayMode.None;
|
|
}
|
|
|
|
switch (mode)
|
|
{
|
|
case ShowcaseDisplayMode.Small when !string.IsNullOrWhiteSpace(snapshot.picName):
|
|
yield return ShowObj(snapshot.picName, 0f);
|
|
break;
|
|
case ShowcaseDisplayMode.Large when !string.IsNullOrWhiteSpace(snapshot.picName):
|
|
yield return ShowLargeObj(snapshot.picName, 0f);
|
|
break;
|
|
}
|
|
|
|
SetBackgroundImmediate(snapshot.isBackgroundVisible);
|
|
}
|
|
|
|
private void NotifyPresentationChanging()
|
|
{
|
|
PresentationChanging?.Invoke();
|
|
}
|
|
|
|
private void ClearSmallImmediate()
|
|
{
|
|
if (_spriteRenderer != null)
|
|
{
|
|
_spriteRenderer.DOKill();
|
|
_spriteRenderer.SetAlpha(0f);
|
|
_spriteRenderer.gameObject.SetActive(false);
|
|
}
|
|
_currentSmallPicName = null;
|
|
}
|
|
|
|
private void ClearLargeImmediate()
|
|
{
|
|
if (_largeSpriteRenderer != null)
|
|
{
|
|
_largeSpriteRenderer.DOKill();
|
|
_largeSpriteRenderer.SetAlpha(0f);
|
|
_largeSpriteRenderer.gameObject.SetActive(false);
|
|
}
|
|
_currentLargePicName = null;
|
|
}
|
|
|
|
private void SetBackgroundImmediate(bool visible)
|
|
{
|
|
if (_backgroundSpriteRenderer == null) return;
|
|
_backgroundSpriteRenderer.DOKill();
|
|
_backgroundSpriteRenderer.SetAlpha(visible ? 1f : 0f);
|
|
_backgroundSpriteRenderer.gameObject.SetActive(visible);
|
|
}
|
|
}
|
|
}
|