Animation控制单条动画播放(手动设置起始帧、结束帧)

📅 发布时间:2026/7/4 18:10:28 👁️ 浏览次数:
Animation控制单条动画播放(手动设置起始帧、结束帧)
代码如下using System.Collections; using UnityEngine; // 强制挂载Animation组件避免忘记添加 [RequireComponent(typeof(Animation))] public class Test : MonoBehaviour { private Animation _anim; // 自身的Animation组件 private AnimationState _animState;// 要控制的单条动画状态核心控制对象 public string animClipName ; // 要控制的动画片段名和Animation组件中一致空则用默认片段 public float animFrameRate 30f; // 【配置】动画帧率Animation窗口查看的数值 private float _startTime; // 实时计算的起始时间秒 private float _stopTime; // 实时计算的停止时间秒 private bool _isPlaying false; // 动画播放状态标记 void Start() { // 初始化组件和动画状态只执行一次无需重复初始化 _anim GetComponentAnimation(); InitAnimState(); // 初始化动画为单次播放 if (_animState ! null) _animState.wrapMode WrapMode.Once; // 【调用示例1】立即播放从0帧开始30帧停止无延时 // StartCoroutine(PlayAnimFromSpecifiedFrame(0, 30)); // 【调用示例2】延时播放延时2秒从5帧开始40帧停止 StartCoroutine(PlayAnimFromSpecifiedFrame(450, 500, 2f)); } void Update() { // 动画正在播放时逐帧检测是否到达停止帧逻辑不变复用实时计算的_stopTime if (_isPlaying _animState ! null) { if (_animState.time _stopTime) { PauseAnim(); Debug.Log($动画已在{Mathf.Round(_stopTime * animFrameRate)}帧停止); } } } #region 初始化AnimationState逻辑完全不变 private void InitAnimState() { if (_anim null || _anim.GetClipCount() 0) { Debug.LogError(请先给Animation组件添加动画片段, gameObject); return; } if (string.IsNullOrEmpty(animClipName)) { animClipName _anim.clip.name; _animState _anim[animClipName]; } else { _animState _anim[animClipName]; if (_animState null) { Debug.LogError($Animation组件中未找到名为{animClipName}的动画片段, gameObject); } } } #endregion #region 核心改造协程版 - 带延时实时帧参数 播放动画 /// summary /// 协程版指定帧区间播放动画支持延时调用实时传帧参数 /// /summary /// param namestartFrame本次播放的起始帧实时传参/param /// param namestopFrame本次播放的停止帧实时传参/param /// param namedelay延时播放时间秒默认0立即播放/param /// returns协程迭代器/returns public IEnumerator PlayAnimFromSpecifiedFrame(int startFrame, int stopFrame, float delay 0f) { // 前置校验动画状态为空/帧参数非法直接退出 if (_animState null) yield break; if (startFrame 0 || stopFrame 0) { Debug.LogError(帧参数不能为负数, gameObject); yield break; } if (startFrame stopFrame) { Debug.LogError(起始帧不能大于停止帧, gameObject); yield break; } // 第一步延时逻辑delay0时等待默认0则直接执行 if (delay 0f) { Debug.Log($动画将延时{delay}秒播放区间{startFrame}帧 → {stopFrame}帧); yield return new WaitForSeconds(delay); } // 第二步实时计算帧对应的时间核心不再依赖Inspector配置随参数变化 _startTime startFrame / animFrameRate; _stopTime stopFrame / animFrameRate; // 第三步停止其他动画重置并播放当前动画重复调用自动重置 _anim.Play(animClipName, PlayMode.StopAll); // 停止其他动画避免冲突 _animState.time _startTime; // 跳转到实时传入的起始帧 _animState.speed 1f; // 恢复播放速度 _isPlaying true; // 更新播放状态 Debug.Log($动画{animClipName}开始播放区间{startFrame}帧 → {stopFrame}帧); } #endregion #region 手动控制播放/暂停/重新播放逻辑完全不变可正常使用 public void PauseAnim() { if (_animState null || !_isPlaying) return; _animState.speed 0f; _isPlaying false; } public void ResumeAnim() { if (_animState null || _isPlaying) return; _animState.speed 1f; _isPlaying true; } /// summary /// 重新播放指定帧区间封装协程调用方便外部直接调用 /// /summary /// param namestartFrame起始帧/param /// param namestopFrame停止帧/param /// param namedelay延时时间/param public void ReplayAnim(int startFrame, int stopFrame, float delay 0f) { StartCoroutine(PlayAnimFromSpecifiedFrame(startFrame, stopFrame, delay)); } #endregion #region 辅助方法帧转时间备用 private float FrameToTime(int frame) { return frame / animFrameRate; } #endregion // 编辑器调试实时显示当前播放帧适配实时传参的帧区间 void OnDrawGizmosSelected() { Gizmos.color Color.red; Gizmos.DrawWireSphere(transform.position Vector3.up * 2, 0.5f); float currentFrame _animState null ? 0 : Mathf.Round(_animState.time * animFrameRate); UnityEditor.Handles.Label(transform.position Vector3.up * 3, $当前帧{currentFrame}\n本次停止帧{Mathf.Round(_stopTime * animFrameRate)}); } }