395 lines
13 KiB
C#
395 lines
13 KiB
C#
using System;
|
|
using System.Collections;
|
|
using AibisDream.Framework;
|
|
using AibisDream.Kit;
|
|
using AibisDream.SaveSystem;
|
|
using AibisDream.Utility;
|
|
using DG.Tweening;
|
|
using RainbowArt.CleanFlatUI;
|
|
using UnityEngine;
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
using UnityEngine.UI;
|
|
|
|
namespace AibisDream.UI
|
|
{
|
|
/// <summary>
|
|
/// 主要用来放置图片展示、窗口和时间流转等演出工具
|
|
/// </summary>
|
|
public class PlayToolPanel : MonoBehaviour, IUIPanel
|
|
{
|
|
public bool IsOpen => gameObject.activeSelf;
|
|
public bool IsCloseable => false;
|
|
|
|
[Header("Obj尺寸参数")] public float objWidth;
|
|
public float objHeight;
|
|
|
|
/// <summary>Addressable Key 模板,与 Obj 精灵 Address 一致。</summary>
|
|
private const string ObjSpritePath = "Sprite/Obj/{0}.png";
|
|
|
|
#region 索引
|
|
|
|
private GameObject _showObjPanel;
|
|
private Image _showObjImage;
|
|
private Image _fullScreenImage;
|
|
private Image _redFlash;
|
|
private ModalWindowProgressBar _window;
|
|
private ModalWindow _warningWindow;
|
|
private SignBoard _signBoard;
|
|
private TimeTurner _timeTurner;
|
|
private int _timeFlowsVersion;
|
|
private int _objOperationVersion;
|
|
private int _fullScreenOperationVersion;
|
|
private string _currentObjPicName;
|
|
private string _currentFullScreenPicName;
|
|
private bool _isObjVisible;
|
|
private bool _isFullScreenVisible;
|
|
|
|
#endregion
|
|
|
|
private void Awake()
|
|
{
|
|
InitRefs();
|
|
}
|
|
|
|
private void InitRefs()
|
|
{
|
|
_showObjPanel = transform.Find("Show Obj").gameObject;
|
|
_showObjImage = _showObjPanel.transform.Find("Obj").GetComponent<Image>();
|
|
_fullScreenImage = transform.Find("Full Screen Image").GetComponent<Image>();
|
|
_window = transform.Find("进度条窗口").GetComponent<ModalWindowProgressBar>();
|
|
_redFlash = transform.Find("Red Flash").GetComponent<Image>();
|
|
_signBoard = transform.GetComponentInChildren<SignBoard>();
|
|
_timeTurner = transform.GetComponentInChildren<TimeTurner>();
|
|
}
|
|
|
|
private static string NormalizeObjPicName(string picName)
|
|
{
|
|
if (string.IsNullOrEmpty(picName)) return picName;
|
|
return picName.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
|
? picName.Substring(0, picName.Length - 4)
|
|
: picName;
|
|
}
|
|
|
|
private IEnumerator LoadObjSprite(
|
|
string picName,
|
|
Action<Sprite> completed,
|
|
Action<string> warning = null)
|
|
{
|
|
var normalizedName = NormalizeObjPicName(picName);
|
|
var key = string.Format(ObjSpritePath, normalizedName);
|
|
var handle = ResourceSystem.LoadAsync<Sprite>(key);
|
|
yield return handle;
|
|
if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
|
|
{
|
|
completed?.Invoke(handle.Result);
|
|
yield break;
|
|
}
|
|
|
|
var message = $"[PlayToolPanel] Failed to load sprite: {key}";
|
|
Debug.LogError(message);
|
|
warning?.Invoke(message);
|
|
completed?.Invoke(null);
|
|
}
|
|
|
|
private void ApplyObjSprite(Sprite sprite)
|
|
{
|
|
var imageAspect = (float)sprite.texture.width / sprite.texture.height;
|
|
if (imageAspect > 1f)
|
|
{
|
|
_showObjImage.rectTransform.sizeDelta = new Vector2(objWidth, objWidth / imageAspect);
|
|
}
|
|
else
|
|
{
|
|
_showObjImage.rectTransform.sizeDelta = new Vector2(objHeight * imageAspect, objHeight);
|
|
}
|
|
|
|
_showObjImage.sprite = sprite;
|
|
}
|
|
|
|
private void ClearObjImmediate()
|
|
{
|
|
_objOperationVersion++;
|
|
_showObjImage.DOKill();
|
|
_showObjImage.SetAlpha(0f);
|
|
_showObjPanel.SetActive(false);
|
|
_currentObjPicName = null;
|
|
_isObjVisible = false;
|
|
}
|
|
|
|
private void ClearFullScreenImmediate()
|
|
{
|
|
_fullScreenOperationVersion++;
|
|
_fullScreenImage.DOKill();
|
|
_fullScreenImage.SetAlpha(0f);
|
|
_fullScreenImage.gameObject.SetActive(false);
|
|
_currentFullScreenPicName = null;
|
|
_isFullScreenVisible = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 显示物体
|
|
/// </summary>
|
|
/// <returns>协程</returns>
|
|
public IEnumerator ShowObj(string picName, float duration = 1)
|
|
{
|
|
var version = ++_objOperationVersion;
|
|
_showObjImage.DOKill();
|
|
_showObjPanel.gameObject.SetActive(true);
|
|
|
|
Sprite newSprite = null;
|
|
yield return LoadObjSprite(picName, value => newSprite = value);
|
|
if (version != _objOperationVersion)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
if (newSprite == null)
|
|
{
|
|
ClearObjImmediate();
|
|
yield break;
|
|
}
|
|
|
|
ApplyObjSprite(newSprite);
|
|
yield return _showObjImage.FadeInAsync(duration);
|
|
if (version != _objOperationVersion) yield break;
|
|
|
|
_currentObjPicName = NormalizeObjPicName(picName);
|
|
_isObjVisible = true;
|
|
}
|
|
|
|
public IEnumerator HideObj(float duration = 1)
|
|
{
|
|
var version = ++_objOperationVersion;
|
|
_currentObjPicName = null;
|
|
_isObjVisible = false;
|
|
_showObjImage.DOKill();
|
|
yield return _showObjImage.FadeOutAsync(duration);
|
|
if (version != _objOperationVersion) yield break;
|
|
_showObjPanel.SetActive(false);
|
|
}
|
|
|
|
public IEnumerator ShowFullScreen(string picName, float duration = 1)
|
|
{
|
|
var version = ++_fullScreenOperationVersion;
|
|
_fullScreenImage.DOKill();
|
|
_fullScreenImage.gameObject.SetActive(true);
|
|
|
|
Sprite newSprite = null;
|
|
yield return LoadObjSprite(picName, value => newSprite = value);
|
|
if (version != _fullScreenOperationVersion)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
if (newSprite == null)
|
|
{
|
|
ClearFullScreenImmediate();
|
|
yield break;
|
|
}
|
|
|
|
_fullScreenImage.sprite = newSprite;
|
|
|
|
yield return _fullScreenImage.FadeInAsync(duration);
|
|
if (version != _fullScreenOperationVersion) yield break;
|
|
|
|
_currentFullScreenPicName = NormalizeObjPicName(picName);
|
|
_isFullScreenVisible = true;
|
|
}
|
|
|
|
public IEnumerator HideFullScreen(float duration = 1)
|
|
{
|
|
var version = ++_fullScreenOperationVersion;
|
|
_currentFullScreenPicName = null;
|
|
_isFullScreenVisible = false;
|
|
_fullScreenImage.DOKill();
|
|
yield return _fullScreenImage.FadeOutAsync(duration);
|
|
if (version != _fullScreenOperationVersion) yield break;
|
|
_fullScreenImage.gameObject.SetActive(false);
|
|
}
|
|
|
|
public PlayToolSnapshotDto CaptureSnapshot()
|
|
{
|
|
return new PlayToolSnapshotDto
|
|
{
|
|
isObjVisible = _isObjVisible,
|
|
objPicName = _currentObjPicName,
|
|
isFullScreenVisible = _isFullScreenVisible,
|
|
fullScreenPicName = _currentFullScreenPicName
|
|
};
|
|
}
|
|
|
|
public IEnumerator RestoreSnapshotAsync(PlayToolSnapshotDto snapshot, Action<string> warning = null)
|
|
{
|
|
ClearObjImmediate();
|
|
ClearFullScreenImmediate();
|
|
if (snapshot == null) yield break;
|
|
|
|
if (snapshot.isObjVisible && !string.IsNullOrWhiteSpace(snapshot.objPicName))
|
|
{
|
|
var version = ++_objOperationVersion;
|
|
Sprite sprite = null;
|
|
yield return LoadObjSprite(snapshot.objPicName, value => sprite = value, warning);
|
|
if (version == _objOperationVersion && sprite != null)
|
|
{
|
|
ApplyObjSprite(sprite);
|
|
_showObjPanel.SetActive(true);
|
|
_showObjImage.SetAlpha(1f);
|
|
_currentObjPicName = NormalizeObjPicName(snapshot.objPicName);
|
|
_isObjVisible = true;
|
|
}
|
|
}
|
|
else if (snapshot.isObjVisible)
|
|
{
|
|
warning?.Invoke("[PlayToolPanel] Obj snapshot is missing picName.");
|
|
}
|
|
|
|
if (snapshot.isFullScreenVisible && !string.IsNullOrWhiteSpace(snapshot.fullScreenPicName))
|
|
{
|
|
var version = ++_fullScreenOperationVersion;
|
|
Sprite sprite = null;
|
|
yield return LoadObjSprite(snapshot.fullScreenPicName, value => sprite = value, warning);
|
|
if (version == _fullScreenOperationVersion && sprite != null)
|
|
{
|
|
_fullScreenImage.sprite = sprite;
|
|
_fullScreenImage.gameObject.SetActive(true);
|
|
_fullScreenImage.SetAlpha(1f);
|
|
_currentFullScreenPicName = NormalizeObjPicName(snapshot.fullScreenPicName);
|
|
_isFullScreenVisible = true;
|
|
}
|
|
}
|
|
else if (snapshot.isFullScreenVisible)
|
|
{
|
|
warning?.Invoke("[PlayToolPanel] Full-screen snapshot is missing picName.");
|
|
}
|
|
}
|
|
|
|
public IEnumerator OpenProgressWindow(string title, string description, float duration)
|
|
{
|
|
AudioManager.Instance.PlaySfx("event:/Scriptal/Ui");
|
|
_window.TitleValue = title;
|
|
_window.DescriptionValue = description;
|
|
float currentProgress = 0;
|
|
|
|
DOTween.To(
|
|
() => currentProgress,
|
|
value =>
|
|
{
|
|
currentProgress = value;
|
|
_window.SetProgress(currentProgress); // 更新进度
|
|
},
|
|
100,
|
|
duration
|
|
);
|
|
|
|
_window.ShowModalWindow();
|
|
|
|
// 获取 moveText 的 RectTransform
|
|
RectTransform moveTextRect =
|
|
_window.transform.Find("View/mask/moveText")?.GetComponent<RectTransform>();
|
|
if (moveTextRect != null)
|
|
{
|
|
moveTextRect.DOAnchorPosY(0f, duration).SetEase(Ease.Linear);
|
|
}
|
|
|
|
// 等待用户确认
|
|
yield return new WaitUntil(() => currentProgress >= 100);
|
|
_window.HideModalWindow();
|
|
moveTextRect.DOAnchorPosY(1326f, 0.1f).SetEase(Ease.Linear);
|
|
}
|
|
|
|
// 红色闪烁效果
|
|
public IEnumerator FlashRed(float duration)
|
|
{
|
|
_redFlash.gameObject.SetActive(true);
|
|
yield return _redFlash.FadeInAsync(duration / 2);
|
|
yield return _redFlash.FadeOutAsync(duration / 2);
|
|
_redFlash.gameObject.SetActive(false);
|
|
}
|
|
|
|
public void ShowSignBoard()
|
|
{
|
|
_signBoard.ShowSignBoard();
|
|
}
|
|
|
|
public void EnableSignBoard()
|
|
{
|
|
_signBoard.EnableSignBoard();
|
|
}
|
|
|
|
public void CloseSignBoard()
|
|
{
|
|
_signBoard.CloseSignBoard();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清理演出层内容,但不影响全局转场面板(用于返回主界面时在遮罩下重置状态)。
|
|
/// </summary>
|
|
public void ClearPresentationEffects()
|
|
{
|
|
_timeFlowsVersion++;
|
|
|
|
// SceneLoader unloads scenes with DOTween.KillAll(). Do not rely on a
|
|
// tween completion callback to deactivate these raycast targets: if
|
|
// that tween is killed, an invisible full-screen image blocks the UI.
|
|
ClearFullScreenImmediate();
|
|
ClearObjImmediate();
|
|
|
|
_timeTurner.ForceStop();
|
|
}
|
|
|
|
// ------------------------------- 时间流转 -------------------------------
|
|
public IEnumerator TimeFlowsOn(string originState, string targetState)
|
|
{
|
|
var version = ++_timeFlowsVersion;
|
|
|
|
var transitionPanel = UIManager.Instance.GetPanel<ScreenTransitionPanel>();
|
|
|
|
// 黑屏淡入
|
|
if (transitionPanel != null)
|
|
{
|
|
yield return transitionPanel.FadeInAsync(0.5f);
|
|
}
|
|
if (version != _timeFlowsVersion) yield break;
|
|
|
|
// 时间流转
|
|
yield return _timeTurner.TurnTimeTo(originState, targetState);
|
|
if (version != _timeFlowsVersion) yield break;
|
|
|
|
// 黑屏淡出
|
|
if (transitionPanel != null)
|
|
{
|
|
yield return transitionPanel.FadeOutAsync(0.5f);
|
|
}
|
|
}
|
|
|
|
#region 感觉这个面板应该常驻
|
|
|
|
public void Show()
|
|
{
|
|
gameObject.SetActive(true);
|
|
}
|
|
|
|
public void Hide()
|
|
{
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 打开警告窗口
|
|
/// </summary>
|
|
/// <param name="description">警告描述文本</param>
|
|
/// <returns>等待玩家确认的协程</returns>
|
|
public IEnumerator OpenWarningWindow(string description)
|
|
{
|
|
AudioManager.Instance.PlaySfx("event:/Scriptal/Ui");
|
|
_warningWindow.DescriptionValue = description;
|
|
_warningWindow.ShowModalWindow();
|
|
|
|
// 等待玩家点击确认按钮
|
|
yield return new WaitUntil(() => !_warningWindow.gameObject.activeSelf);
|
|
}
|
|
}
|
|
}
|