using System; using System.Threading; using System.Threading.Tasks; using UnityEngine; using Cysharp.Threading.Tasks; /// /// CanvasGroup渐变控制器 /// 使用async/await实现平滑的alpha值渐变效果 /// public class CanvasGroupController : MonoBehaviour { [Header("渐变设置")] [SerializeField] private float fadeInDuration = 0.3f; // 渐变显示时长 [SerializeField] private float fadeOutDuration = 0.3f; // 渐变隐藏时长 [SerializeField] private EaseType easeType = EaseType.EaseInOut; // 缓动类型 [Header("组件引用")] [SerializeField] private CanvasGroup canvasGroup; // CanvasGroup组件 // 缓动类型枚举 public enum EaseType { Linear, // 线性 EaseIn, // 缓入 EaseOut, // 缓出 EaseInOut // 缓入缓出 } // 渐变完成事件 public event Action OnFadeInComplete; public event Action OnFadeOutComplete; private CancellationTokenSource _cancellationTokenSource; private bool _isFading = false; void Awake() { // 如果没有指定CanvasGroup,自动获取 if (canvasGroup == null) { canvasGroup = GetComponent(); if (canvasGroup == null) { canvasGroup = gameObject.AddComponent(); Debug.Log($"为 {gameObject.name} 自动添加了CanvasGroup组件"); } } // 初始化alpha值为0 canvasGroup.alpha = 0f; canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; } /// /// 渐变显示(alpha 0→1) /// /// 渐变时长,如果为0则使用默认值 /// 缓动类型,如果为null则使用默认值 /// 目标CanvasGroup,如果为null则使用当前对象的CanvasGroup /// 异步任务 public async UniTask FadeInAsync(float duration = 0f, EaseType? easeType = null, CanvasGroup targetCanvasGroup = null) { if (_isFading) { Debug.LogWarning($"{gameObject.name} 正在渐变中,忽略新的渐变请求"); return; } // 取消之前的渐变 CancelCurrentFade(); _isFading = true; float targetDuration = duration > 0 ? duration : fadeInDuration; EaseType targetEaseType = easeType ?? this.easeType; // 确定目标CanvasGroup CanvasGroup targetGroup = targetCanvasGroup ?? canvasGroup; if (targetGroup == null) { Debug.LogError($"{gameObject.name} 没有可用的CanvasGroup组件"); _isFading = false; return; } Debug.Log($"{gameObject.name} 开始渐变显示,目标:{targetGroup.gameObject.name},时长:{targetDuration}秒,缓动类型:{targetEaseType}"); try { await FadeAlphaAsync(0f, 1f, targetDuration, targetEaseType, targetGroup); // 渐变完成后的设置 targetGroup.interactable = true; targetGroup.blocksRaycasts = true; OnFadeInComplete?.Invoke(); Debug.Log($"{gameObject.name} 渐变显示完成,目标:{targetGroup.gameObject.name}"); } catch (OperationCanceledException) { Debug.Log($"{gameObject.name} 渐变显示被取消"); } finally { _isFading = false; } } /// /// 渐变隐藏(alpha 1→0) /// /// 渐变时长,如果为0则使用默认值 /// 缓动类型,如果为null则使用默认值 /// 目标CanvasGroup,如果为null则使用当前对象的CanvasGroup /// 异步任务 public async UniTask FadeOutAsync(float duration = 0f, EaseType? easeType = null, CanvasGroup targetCanvasGroup = null) { if (_isFading) { Debug.LogWarning($"{gameObject.name} 正在渐变中,忽略新的渐变请求"); return; } // 取消之前的渐变 CancelCurrentFade(); _isFading = true; float targetDuration = duration > 0 ? duration : fadeOutDuration; EaseType targetEaseType = easeType ?? this.easeType; // 确定目标CanvasGroup CanvasGroup targetGroup = targetCanvasGroup ?? canvasGroup; if (targetGroup == null) { Debug.LogError($"{gameObject.name} 没有可用的CanvasGroup组件"); _isFading = false; return; } Debug.Log($"{gameObject.name} 开始渐变隐藏,目标:{targetGroup.gameObject.name},时长:{targetDuration}秒,缓动类型:{targetEaseType}"); try { await FadeAlphaAsync(1f, 0f, targetDuration, targetEaseType, targetGroup); // 渐变完成后的设置 targetGroup.interactable = false; targetGroup.blocksRaycasts = false; OnFadeOutComplete?.Invoke(); Debug.Log($"{gameObject.name} 渐变隐藏完成,目标:{targetGroup.gameObject.name}"); } catch (OperationCanceledException) { Debug.Log($"{gameObject.name} 渐变隐藏被取消"); } finally { _isFading = false; } } /// /// 核心渐变方法 /// private async UniTask FadeAlphaAsync(float fromAlpha, float toAlpha, float duration, EaseType easeType, CanvasGroup targetCanvasGroup) { _cancellationTokenSource = new CancellationTokenSource(); var token = _cancellationTokenSource.Token; float elapsedTime = 0f; targetCanvasGroup.alpha = fromAlpha; while (elapsedTime < duration) { if (token.IsCancellationRequested) { throw new OperationCanceledException(); } elapsedTime += Time.deltaTime; float progress = elapsedTime / duration; float easedProgress = ApplyEasing(progress, easeType); targetCanvasGroup.alpha = Mathf.Lerp(fromAlpha, toAlpha, easedProgress); await UniTask.Yield(PlayerLoopTiming.Update, token); } // 确保最终值准确 targetCanvasGroup.alpha = toAlpha; } /// /// 应用缓动函数 /// private float ApplyEasing(float t, EaseType easeType) { switch (easeType) { case EaseType.Linear: return t; case EaseType.EaseIn: return t * t; case EaseType.EaseOut: return 1f - (1f - t) * (1f - t); case EaseType.EaseInOut: return t < 0.5f ? 2f * t * t : 1f - 2f * (1f - t) * (1f - t); default: return t; } } /// /// 取消当前渐变 /// public void CancelCurrentFade() { if (_cancellationTokenSource != null) { _cancellationTokenSource.Cancel(); _cancellationTokenSource.Dispose(); _cancellationTokenSource = null; } } /// /// 立即设置alpha值(无渐变) /// /// 目标alpha值 /// 目标CanvasGroup,如果为null则使用当前对象的CanvasGroup public void SetAlphaImmediate(float alpha, CanvasGroup targetCanvasGroup = null) { CancelCurrentFade(); CanvasGroup targetGroup = targetCanvasGroup ?? canvasGroup; if (targetGroup == null) { Debug.LogError($"{gameObject.name} 没有可用的CanvasGroup组件"); return; } targetGroup.alpha = Mathf.Clamp01(alpha); targetGroup.interactable = alpha > 0.5f; targetGroup.blocksRaycasts = alpha > 0.5f; } /// /// 检查是否正在渐变中 /// public bool IsFading => _isFading; /// /// 获取当前alpha值 /// public float CurrentAlpha => canvasGroup != null ? canvasGroup.alpha : 0f; /// /// 获取指定CanvasGroup的alpha值 /// /// 目标CanvasGroup /// alpha值,如果目标为null则返回0 public float GetAlpha(CanvasGroup targetCanvasGroup) { return targetCanvasGroup != null ? targetCanvasGroup.alpha : 0f; } void OnDestroy() { CancelCurrentFade(); } }