如何让Unity UI淡入淡出从当前进度接续而非重新开始?
问题根源
原代码存在两个核心问题:
- 每次触发淡入淡出都会启动新协程,旧协程仍在后台运行,导致透明度被多个协程同时修改,出现混乱
- 每次启动动画时强制将透明度重置为淡入/淡出的起始值(0或1),完全忽略当前的透明度状态
修复后的淡入淡出脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; public class Description : MonoBehaviour { public Canvas canvas; public AnimationCurve animationCurve; public float fadingSpeed = 5f; public TMP_InputField _inputField; public enum Direction { FadeIn, FadeOut }; private CanvasGroup canvasGroup; private Coroutine currentFadeCoroutine; // 跟踪当前运行的淡入淡出协程 void Start() { if (canvas == null) canvas = GetComponent<Canvas>(); canvasGroup = canvas.GetComponent<CanvasGroup>(); if (canvasGroup == null) Debug.LogError("请给画布添加CanvasGroup组件!"); if (animationCurve.length == 0) { Debug.Log("未指定动画曲线,将创建默认缓入缓出曲线"); animationCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); } } public float StartFading(bool InOut) { if (canvasGroup != null) { // 停止当前协程,避免多个协程冲突 if (currentFadeCoroutine != null) { StopCoroutine(currentFadeCoroutine); } Direction direction = InOut ? Direction.FadeIn : Direction.FadeOut; currentFadeCoroutine = StartCoroutine(FadeCanvas(canvasGroup, direction, fadingSpeed)); } return fadingSpeed; } public IEnumerator FadeCanvas(CanvasGroup canvasGroup, Direction direction, float totalDuration) { // 从当前透明度反向计算对应的动画曲线进度 float currentAlpha = canvasGroup.alpha; float currentProgress = GetCurrentProgressFromAlpha(currentAlpha); // 计算剩余需要完成的进度和对应时长 float targetProgress = direction == Direction.FadeIn ? 1f : 0f; float progressDelta = Mathf.Abs(targetProgress - currentProgress); float remainingDuration = totalDuration * progressDelta; if (remainingDuration <= 0f) { canvasGroup.alpha = animationCurve.Evaluate(targetProgress); _inputField?.SetReadOnly(false); yield break; } var startTime = Time.time; var endTime = startTime + remainingDuration; while (Time.time <= endTime) { float elapsedTime = Time.time - startTime; float percentage = elapsedTime / remainingDuration; float newProgress = Mathf.Lerp(currentProgress, targetProgress, percentage); canvasGroup.alpha = animationCurve.Evaluate(newProgress); yield return new WaitForEndOfFrame(); } // 确保最终透明度准确 canvasGroup.alpha = animationCurve.Evaluate(targetProgress); _inputField?.SetReadOnly(false); currentFadeCoroutine = null; } // 根据当前透明度反向查找对应的动画曲线进度 private float GetCurrentProgressFromAlpha(float alpha) { float closestProgress = 0f; float closestAlphaDiff = Mathf.Infinity; // 先找最接近的关键帧进度 foreach (Keyframe keyframe in animationCurve.keys) { float diff = Mathf.Abs(keyframe.value - alpha); if (diff < closestAlphaDiff) { closestAlphaDiff = diff; closestProgress = keyframe.time; } } // 处理关键帧之间的插值情况 for (int i = 0; i < animationCurve.keys.Length - 1; i++) { Keyframe start = animationCurve.keys[i]; Keyframe end = animationCurve.keys[i + 1]; if ((alpha >= start.value && alpha <= end.value) || (alpha <= start.value && alpha >= end.value)) { float alphaRange = end.value - start.value; if (alphaRange == 0) continue; float alphaPercent = (alpha - start.value) / alphaRange; closestProgress = Mathf.Lerp(start.time, end.time, alphaPercent); break; } } return Mathf.Clamp01(closestProgress); } }
关键修改说明
- 新增
currentFadeCoroutine变量,每次触发新动画时先停止旧协程,避免多个协程同时修改透明度 - 移除强制重置起始透明度的逻辑,改为从当前透明度反向计算动画进度
- 添加
GetCurrentProgressFromAlpha方法,实现从透明度到动画曲线进度的反向映射,保证动画从当前状态续接 - 动态计算剩余动画时长,确保淡入淡出的速度始终一致
触发脚本无需修改
原ButtonHover脚本的逻辑保持不变即可,所有中断续接逻辑已在Description脚本中处理。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

