WX-Game1/Assets/Scripts/UI/CanvasGroupController.cs

273 lines
9.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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