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
{
///
/// 主要用来放置图片展示、窗口和时间流转等演出工具
///
public class PlayToolPanel : MonoBehaviour, IUIPanel
{
public bool IsOpen => gameObject.activeSelf;
public bool IsCloseable => false;
[Header("Obj尺寸参数")] public float objWidth;
public float objHeight;
/// Addressable Key 模板,与 Obj 精灵 Address 一致。
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();
_fullScreenImage = transform.Find("Full Screen Image").GetComponent();
_window = transform.Find("进度条窗口").GetComponent();
_redFlash = transform.Find("Red Flash").GetComponent();
_signBoard = transform.GetComponentInChildren();
_timeTurner = transform.GetComponentInChildren();
}
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 completed,
Action warning = null)
{
var normalizedName = NormalizeObjPicName(picName);
var key = string.Format(ObjSpritePath, normalizedName);
var handle = ResourceSystem.LoadAsync(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;
}
///
/// 显示物体
///
/// 协程
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 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();
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();
}
///
/// 清理演出层内容,但不影响全局转场面板(用于返回主界面时在遮罩下重置状态)。
///
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();
// 黑屏淡入
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
///
/// 打开警告窗口
///
/// 警告描述文本
/// 等待玩家确认的协程
public IEnumerator OpenWarningWindow(string description)
{
AudioManager.Instance.PlaySfx("event:/Scriptal/Ui");
_warningWindow.DescriptionValue = description;
_warningWindow.ShowModalWindow();
// 等待玩家点击确认按钮
yield return new WaitUntil(() => !_warningWindow.gameObject.activeSelf);
}
}
}