Unity3D 利用代码在动画片断中添加变乱

[复制链接]
发表于 2025-11-19 12:26:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
Unity3D 利用代码在动画片断中添加变乱

在 Unity 中,动画片断(AnimationClip)可以通过添加变乱(AnimationEvent)来在特定时间点触发自界说函数。这种方式非常恰当在动画播放过程中实行一些逻辑利用,比方播放音效、触发粒子结果、大概控制游戏逻辑。
本文将具体先容怎样利用代码在动画片断中添加变乱,并提供完备的示例代码和优化发起。
通例的在动画中添加变乱的方式:

1. 动画变乱

动画变乱(AnimationEvent)是 Unity 中用于在动画播放过程中触发特定函数的一种机制。你可以在动画片断中的恣意时间点添加变乱,并指定变乱触发时要调用的函数名称。变乱可以携带参数,而且可以在动画编辑器中可视化地编辑。
2. 利用代码添加动画变乱


  • 获取动画片断:通过 Animator 组件获取对应的动画片断。
  • 创建动画变乱:创建一个 AnimationEvent 实例,并设置其触发时间和函数名称。
  • 将变乱添加到动画片断:将创建的变乱添加到动画片断的变乱列表中。
2.1 获取动画片断

我们须要从 Animator 组件中获取对应的动画片断。可以通过遍历 AnimatorController 中的 animationClips 来找到指定名称的动画片断。
  1. public static AnimationClip GetClipFromAnimator(Animator animator, string stateName)
  2. {
  3.     if (animator == null)
  4.     {
  5.         Debug.LogError("Animator is null!");
  6.         return null;
  7.     }
  8.     var animatorController = animator.runtimeAnimatorController;
  9.     if (animatorController == null)
  10.     {
  11.         Debug.LogError("Animator Controller is null!");
  12.         return null;
  13.     }
  14.     foreach (var clip in animatorController.animationClips)
  15.     {
  16.         if (clip.name == stateName)
  17.         {
  18.             return clip;
  19.         }
  20.     }
  21.     Debug.Log($"没有找到名为 {stateName} 的动画片段!");
  22.     return null;
  23. }
复制代码
2.2 创建并添加动画变乱

接下来,我们可以创建一个 AnimationEvent 实例,并将其添加到动画片断中。我们可以选择利用时间(秒)或帧数来指定变乱的触发时间。
  1. public static void AddEventToClip(AnimationClip clip, float time, string functionName, bool useFrames = false)
  2. {
  3.     if (clip == null)
  4.     {
  5.         Debug.LogError("AnimationClip is null!");
  6.         return;
  7.     }
  8.     float fps = clip.frameRate > 0 ? clip.frameRate : 60f; // 默认 60fps
  9.     if (useFrames)
  10.     {
  11.         time = time / fps; // 将帧数转换为秒数
  12.     }
  13.     if (time < 0f)
  14.     {
  15.         time = 0f;
  16.     }
  17.     if (time > clip.length)
  18.     {
  19.         time = clip.length;
  20.     }
  21.     AnimationEvent animationEvent = new AnimationEvent
  22.     {
  23.         time = time,
  24.         functionName = functionName
  25.     };
  26.     clip.AddEvent(animationEvent);
  27. }
复制代码
2.3 清空或删除动画变乱

偶然候我们须要清空或删除动画片断中的特定变乱。可以通过以下方法实现:
  1. public static void ClearAllEventsFromClip(AnimationClip clip)
  2. {
  3.     if (clip == null)
  4.     {
  5.         Debug.LogError("ClearAllEventsFromClip: AnimationClip is null!");
  6.         return;
  7.     }
  8.     clip.events = new AnimationEvent[0];
  9.     Debug.Log($"ClearAllEventsFromClip: All events cleared from clip '{clip.name}'.");
  10. }
  11. public static void RemoveEventFromClip(AnimationClip clip, string functionName)
  12. {
  13.     if (clip == null)
  14.     {
  15.         Debug.LogError("RemoveEventFromClip: AnimationClip is null!");
  16.         return;
  17.     }
  18.     if (string.IsNullOrEmpty(functionName))
  19.     {
  20.         Debug.LogError("RemoveEventFromClip: Function name is null or empty!");
  21.         return;
  22.     }
  23.     AnimationEvent[] events = clip.events;
  24.     var filteredEvents = new System.Collections.Generic.List<AnimationEvent>();
  25.     foreach (var evt in events)
  26.     {
  27.         if (evt.functionName != functionName)
  28.         {
  29.             filteredEvents.Add(evt);
  30.         }
  31.     }
  32.     clip.events = filteredEvents.ToArray();
  33. }
复制代码
3. 示例代码

代码

利用的示例,展示了如安在动画片断中添加变乱,并在变乱触发时实行自界说逻辑。
将这个脚本挂在在带有Animator 组件的物体上然后就可以在动态添加变乱了。
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class ObjAni : MonoBehaviour
  5. {
  6.     private Animator animator;
  7.     private void Start()
  8.     {
  9.         animator = gameObject.GetComponent<Animator>();
  10.         var clip = AnimationHelper.GetClipFromAnimator(animator, "DoRotate");
  11.         // 在第30帧添加事件
  12.         AnimationHelper.AddEventToClip(clip, 30, nameof(AnimatorEventAdd), true);
  13.         animator.SetBool("work", true);
  14.         Invoke(nameof(RemoveEventClip), 3.0f);
  15.     }
  16.     private void AnimatorEventAdd()
  17.     {
  18.         animator.SetBool("work", false);
  19.         Debug.Log("AnimatorEventAdd");
  20.     }
  21.     void RemoveEventClip()
  22.     {
  23.         var clip = AnimationHelper.GetClipFromAnimator(animator, "DoRotate");
  24.         AnimationHelper.RemoveEventFromClip(clip, nameof(AnimatorEventAdd));
  25.         animator.SetBool("work", true);
  26.     }
  27. }
复制代码

  • Start 方法:在游戏开始时,获取 Animator 组件和对应的动画片断 DoRotate,并在第30帧添加一个变乱,变乱触发时调用 AnimatorEventAdd 方法。
  • AnimatorEventAdd 方法:当变乱触发时,克制动画播放,并输出日记信息。
  • RemoveEventClip 方法:在3秒后移除动画片断中的变乱,并重新开始动画播放。
Unity3D动画设置




  • 创建一个空的状态
  • 添加一个切换条件,范例为bool范例,默认是false
  • 添加自己要实行的动画,并修改名称为DoRotate
  • 添加切换条件:new State -true->  DoRotate , DoRotate -false->new State
  • 将这个动画状态机给到上面的Animator  组件
4. 其他发起


  • 性能优化:频仍地添加和删除变乱大概会影响性能,发起在游戏初始化时一次性添加全部变乱,并在须要时举行更新。
  • 变乱参数:AnimationEvent 可以携带参数(如 int、float、string 等),可以根据须要转达更多信息给变乱处置处罚函数。
  • 没有动画:确认导入的模子上有动画,勾选inport Animation

5.完备的添加变乱代码
  1. using UnityEngine;
  2. public static class AnimationHelper
  3. {
  4.     /// <summary>
  5.     /// 获取Animator状态对应的动画片段
  6.     /// </summary>
  7.     /// <param name="animator">Animator组件</param>
  8.     /// <param name="stateName">状态名(动画片段名)</param>
  9.     /// <returns>对应状态的AnimationClip</returns>
  10.     public static AnimationClip GetClipFromAnimator(Animator animator, string stateName)
  11.     {
  12.         if (animator == null)
  13.         {
  14.             Debug.LogError("Animator is null!");
  15.             return null;
  16.         }
  17.         // 获取 Animator 的 Controller
  18.         var animatorController = animator.runtimeAnimatorController;
  19.         if (animatorController == null)
  20.         {
  21.             Debug.LogError("Animator Controller is null!");
  22.             return null;
  23.         }
  24.         // 获取所有动画片段
  25.         foreach (var clip in animatorController.animationClips)
  26.         {
  27.             if (clip.name == stateName)
  28.             {
  29.                 return clip;
  30.             }
  31.         }
  32.         Debug.Log($"没有找到名为 {stateName} 的动画片段!");
  33.         return null;
  34.     }
  35.     /// <summary>
  36.     /// 向指定的动画剪辑添加事件
  37.     /// </summary>
  38.     /// <param name="clip">要添加事件的动画剪辑</param>
  39.     /// <param name="time">事件触发的时间点(单位:秒)</param>
  40.     /// <param name="functionName">事件触发时调用的函数名称</param>
  41.     /// <param name="useFrames">是否使用帧数来设置事件时间(如果为 true,则 `time` 参数会被解释为帧数)</param>
  42.     public static void AddEventToClip(AnimationClip clip, float time, string functionName, bool useFrames = false)
  43.     {
  44.         if (clip == null)
  45.         {
  46.             Debug.LogError("AnimationClip is null!");
  47.             return;
  48.         }
  49.         // 获取动画的帧率
  50.         float fps = clip.frameRate > 0 ? clip.frameRate : 60f; // 默认 60fps
  51.         if (useFrames)
  52.         {
  53.             // 如果使用帧数,将帧数转换为时间(秒)
  54.             time = time / fps; // 将帧数转换为秒数
  55.         }
  56.         // 如果时间小于0,则将其修正为0(动画开始时间)
  57.         if (time < 0f)
  58.         {
  59.             time = 0f;
  60.         }
  61.         // 如果时间超过动画长度,则将其修正为动画的结束时间
  62.         if (time > clip.length)
  63.         {
  64.             time = clip.length;
  65.         }
  66.         // 创建一个新的动画事件实例
  67.         AnimationEvent animationEvent = new AnimationEvent
  68.         {
  69.             // 设置事件的触发时间
  70.             time = time,
  71.             // 设置事件触发时调用的函数名称
  72.             functionName = functionName
  73.         };
  74.         // 将事件添加到动画剪辑中
  75.         clip.AddEvent(animationEvent);
  76.     }
  77.     /// <summary>
  78.     /// 清空动画剪辑中的所有事件
  79.     /// </summary>
  80.     /// <param name="clip">目标动画剪辑</param>
  81.     public static void ClearAllEventsFromClip(AnimationClip clip)
  82.     {
  83.         if (clip == null)
  84.         {
  85.             Debug.LogError("ClearAllEventsFromClip: AnimationClip is null!");
  86.             return;
  87.         }
  88.         clip.events = new AnimationEvent[0];
  89.         Debug.Log($"ClearAllEventsFromClip: All events cleared from clip '{clip.name}'.");
  90.     }
  91.     /// <summary>
  92.     /// 删除动画剪辑中指定函数名称的事件
  93.     /// </summary>
  94.     /// <param name="clip">目标动画剪辑</param>
  95.     /// <param name="functionName">要删除的事件函数名称</param>
  96.     public static void RemoveEventFromClip(AnimationClip clip, string functionName)
  97.     {
  98.         if (clip == null)
  99.         {
  100.             Debug.LogError("RemoveEventFromClip: AnimationClip is null!");
  101.             return;
  102.         }
  103.         if (string.IsNullOrEmpty(functionName))
  104.         {
  105.             Debug.LogError("RemoveEventFromClip: Function name is null or empty!");
  106.             return;
  107.         }
  108.         // 获取当前所有事件
  109.         AnimationEvent[] events = clip.events;
  110.         // 过滤掉指定的事件
  111.         var filteredEvents = new System.Collections.Generic.List<AnimationEvent>();
  112.         foreach (var evt in events)
  113.         {
  114.             if (evt.functionName != functionName)
  115.             {
  116.                 filteredEvents.Add(evt);
  117.             }
  118.         }
  119.         // 将过滤后的事件重新赋值给动画片段
  120.         clip.events = filteredEvents.ToArray();
  121.     }
  122. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表