273 lines
9.0 KiB
C#
273 lines
9.0 KiB
C#
using System;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using Cysharp.Threading.Tasks;
|
||
|
||
/// <summary>
|
||
/// CanvasGroup渐变控制器
|
||
/// 使用async/await实现平滑的alpha值渐变效果
|
||
/// </summary>
|
||
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<CanvasGroup>();
|
||
if (canvasGroup == null)
|
||
{
|
||
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||
Debug.Log($"为 {gameObject.name} 自动添加了CanvasGroup组件");
|
||
}
|
||
}
|
||
|
||
// 初始化alpha值为0
|
||
canvasGroup.alpha = 0f;
|
||
canvasGroup.interactable = false;
|
||
canvasGroup.blocksRaycasts = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 渐变显示(alpha 0→1)
|
||
/// </summary>
|
||
/// <param name="duration">渐变时长,如果为0则使用默认值</param>
|
||
/// <param name="easeType">缓动类型,如果为null则使用默认值</param>
|
||
/// <param name="targetCanvasGroup">目标CanvasGroup,如果为null则使用当前对象的CanvasGroup</param>
|
||
/// <returns>异步任务</returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 渐变隐藏(alpha 1→0)
|
||
/// </summary>
|
||
/// <param name="duration">渐变时长,如果为0则使用默认值</param>
|
||
/// <param name="easeType">缓动类型,如果为null则使用默认值</param>
|
||
/// <param name="targetCanvasGroup">目标CanvasGroup,如果为null则使用当前对象的CanvasGroup</param>
|
||
/// <returns>异步任务</returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 核心渐变方法
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用缓动函数
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消当前渐变
|
||
/// </summary>
|
||
public void CancelCurrentFade()
|
||
{
|
||
if (_cancellationTokenSource != null)
|
||
{
|
||
_cancellationTokenSource.Cancel();
|
||
_cancellationTokenSource.Dispose();
|
||
_cancellationTokenSource = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 立即设置alpha值(无渐变)
|
||
/// </summary>
|
||
/// <param name="alpha">目标alpha值</param>
|
||
/// <param name="targetCanvasGroup">目标CanvasGroup,如果为null则使用当前对象的CanvasGroup</param>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查是否正在渐变中
|
||
/// </summary>
|
||
public bool IsFading => _isFading;
|
||
|
||
/// <summary>
|
||
/// 获取当前alpha值
|
||
/// </summary>
|
||
public float CurrentAlpha => canvasGroup != null ? canvasGroup.alpha : 0f;
|
||
|
||
/// <summary>
|
||
/// 获取指定CanvasGroup的alpha值
|
||
/// </summary>
|
||
/// <param name="targetCanvasGroup">目标CanvasGroup</param>
|
||
/// <returns>alpha值,如果目标为null则返回0</returns>
|
||
public float GetAlpha(CanvasGroup targetCanvasGroup)
|
||
{
|
||
return targetCanvasGroup != null ? targetCanvasGroup.alpha : 0f;
|
||
}
|
||
|
||
void OnDestroy()
|
||
{
|
||
CancelCurrentFade();
|
||
}
|
||
}
|