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();
}
}