using System; using DG.Tweening; // 引入 DOTween using UnityEngine; using SLSUtilities.UI; namespace Cielonos.Core.UI { // 继承 UIElementBase,同时实现简单的单例逻辑 public class ScreenFader : UIElementBase { public static ScreenFader Instance { get; private set; } [Header("Settings")] [SerializeField] private float defaultDuration = 0.5f; [SerializeField] private bool startBlack = true; [SerializeField] private Ease fadeEase = Ease.InOutQuad; // 暴露曲线设置,比 SmoothStep 更灵活 private Tween currentTween; // 记录当前动画,防止冲突 private void Awake() { // --- 单例逻辑 --- if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; // ---------------- // 初始化状态 if (canvasGroup == null) canvasGroup = GetComponent(); if (startBlack) { canvasGroup.alpha = 1f; canvasGroup.blocksRaycasts = true; } else { canvasGroup.alpha = 0f; canvasGroup.blocksRaycasts = false; } } private void OnDestroy() { // 这是一个好习惯:销毁时杀掉所有未完成的动画,防止报错 currentTween?.Kill(); } /// /// 变黑 (Fade Out / 遮挡屏幕) /// public Tween FadeToBlack(float duration = -1, Action onComplete = null) { return FadeTo(1f, duration, onComplete); } /// /// 变亮 (Fade In / 显示屏幕) /// public Tween FadeToClear(float duration = -1, float delay = 1, Action onComplete = null) { return FadeTo(0f, duration, onComplete).SetDelay(delay); } /// /// 通用淡入淡出逻辑 /// 返回 Tween 对象,允许外部使用 yield return tween.WaitForCompletion(); /// private Tween FadeTo(float targetAlpha, float duration, Action onComplete) { // 1. 如果有正在运行的动画,立刻杀掉,确保从当前 alpha 开始新的变化 currentTween?.Kill(); float d = duration < 0 ? defaultDuration : duration; // 2. 只有在变黑(alpha -> 1)开始时,才阻挡射线 // 这样防止玩家在变黑过程中还能点击后面的按钮 if (targetAlpha > 0.5f) { canvasGroup.blocksRaycasts = true; } // 3. 使用 DOTween currentTween = canvasGroup.DOFade(targetAlpha, d) .SetEase(fadeEase) .SetUpdate(true) // 【关键】忽略 TimeScale,游戏暂停时依然可以淡入淡出 .OnComplete(() => { // 只有在完全变亮(alpha -> 0)结束后,才取消阻挡 if (targetAlpha < 0.01f) { canvasGroup.blocksRaycasts = false; } onComplete?.Invoke(); }); return currentTween; } } }