Unity如何通过反向播放中断正在运行的动画
问题背景
某GameObject上存在两个变换状态A和B,同时配置了两个用于在两状态间过渡的动画:A->B与B->A,两个动画采用不同的动画曲线,用户可在任意时刻修改该GameObject的状态。
实现目标
- 无动画播放时修改状态:正常播放
A->B或B->A动画即可 - 动画播放过程中修改状态:将当前正在播放的动画反向播放实现中断,而非切换到对应方向的另一段动画
举例说明:用户将状态切换为B时开始播放
A->B动画,若该动画播放完成前用户突然切回A状态,此时需要A->B动画直接反向播放回到A状态,而非播放B->A动画。
当前进展
目前已尝试使用Unity原生Animator组件,但未找到可反向播放正在运行动画的相关接口;除Animator外也可接受其他原生实现方案,优先不使用任何外部第三方库。
当前仅实现A、B状态切换、未加入反向播放逻辑的代码如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestAnimation : MonoBehaviour { private Animator _animator; private bool _isOpen = true; void Awake() { this._animator = GetComponent<Animator>(); } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { if (this._isOpen) this._animator.SetTrigger("A"); else this._animator.SetTrigger("B"); this._isOpen = !this._isOpen; } } }
实现方案
Animator状态机的状态切换逻辑是预定义的,动态修改当前播放动画的方向实现成本很高,直接用Unity原生Animation组件做手动播放控制是最简洁的方案,完全不需要第三方库。
实现逻辑
- 挂载Animation组件,关闭自动播放,将两个动画片段注册到组件中
- 维护当前播放的动画片段、播放速度两个核心状态
- 触发状态切换时做分支判断:
- 无正在播放的动画:直接播放对应方向的动画片段,速度设为正向1
- 有正在播放的动画:不切换动画片段,直接将当前动画的播放速度取反,动画会自动沿着原曲线从当前进度反向播放
- 每帧检测动画播放进度,正向播到末尾或反向退到起点时停止动画,校正最终状态避免浮点误差
完整代码
using UnityEngine; public class TestAnimation : MonoBehaviour { private Animation _animation; // 在Inspector面板拖入两个方向的动画片段 public AnimationClip a2bClip; public AnimationClip b2aClip; private bool _isOpen = true; private AnimationClip _currentPlayingClip; private float _currentSpeed; void Awake() { _animation = GetComponent<Animation>(); _animation.playAutomatically = false; // 注册动画片段 _animation.AddClip(a2bClip, "A2B"); _animation.AddClip(b2aClip, "B2A"); // 初始状态对齐到A状态 _animation["A2B"].time = 0; _animation.Sample(); _animation.Stop(); } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { ToggleState(); } CheckAnimationComplete(); } void ToggleState() { _isOpen = !_isOpen; // 无播放中动画,启动对应方向的新动画 if (_currentPlayingClip == null) { if (!_isOpen) { _currentPlayingClip = a2bClip; _currentSpeed = 1f; _animation.Play("A2B"); _animation["A2B"].speed = _currentSpeed; _animation["A2B"].time = 0; } else { _currentPlayingClip = b2aClip; _currentSpeed = 1f; _animation.Play("B2A"); _animation["B2A"].speed = _currentSpeed; _animation["B2A"].time = 0; } } // 有播放中动画,直接反向速度 else { _currentSpeed *= -1; string animName = _currentPlayingClip == a2bClip ? "A2B" : "B2A"; _animation[animName].speed = _currentSpeed; } } void CheckAnimationComplete() { if (_currentPlayingClip == null) return; string animName = _currentPlayingClip == a2bClip ? "A2B" : "B2A"; AnimationState animState = _animation[animName]; float totalLength = _currentPlayingClip.length; // 判定播放结束 if ((_currentSpeed > 0 && animState.time >= totalLength) || (_currentSpeed < 0 && animState.time <= 0)) { _animation.Stop(); // 校正位置避免浮点偏移 animState.time = _currentSpeed > 0 ? totalLength : 0; _animation.Sample(); _currentPlayingClip = null; } } }
配置注意事项
- 两个动画片段的
Wrap Mode需要设置为Once,不要开启循环 - 如果不需要保留两个独立曲线的过渡动画,也可以只用一个动画片段,反向时直接倒播,代码逻辑可以进一步简化
- 所有逻辑均使用Unity原生API实现,无额外依赖,性能表现优于Animator状态机方案
内容的提问来源于stack exchange,提问作者Adam Jonsson
相关产品推荐
相关产品推荐

