在 Unity 中以编程方式创建动画?

Posted

技术标签:

【中文标题】在 Unity 中以编程方式创建动画?【英文标题】:Create animations programmatically in Unity? 【发布时间】:2021-05-17 01:55:22 【问题描述】:

在我的游戏中,我有一个很大的装备目录:盔甲、武器和盾牌。这些之间的组合可能非常巨大。

除此之外,玩家还可以选择在游戏中切换到不同的盔甲武器组合。最后为了解决这个问题,我使用了以下对象结构。

每当我切换武器时,我都会激活/停用必要的游戏对象。动画是这样设置的:

现在,问题在于创建动画。我首先考虑以编程方式预渲染所有组合,但我的目录非常庞大,以至于它会创建 100 多个动画,如果不是 1000 多个动画。所以我选择了不同的解决方案。一旦我知道玩家会选择什么装备,就可以在播放时间创建动画。为此,我创建了一个脚本来处理它。问题是我一直在使用来自UnityEditor 的 API,现在我意识到构建将无法正常工作。特别是因为 2 个不同的类:EditorCurveBindingObjectReferenceKeyframe

这是我在创建动画时如何使用这些类的几个 sn-ps:

static EditorCurveBinding GetEditorCurveBinding(string path = "")

    EditorCurveBinding spriteBinding = new EditorCurveBinding();
    spriteBinding.type = typeof(SpriteRenderer);
    spriteBinding.path = path;
    spriteBinding.propertyName = "m_Sprite";

    return spriteBinding;

static ObjectReferenceKeyframe GetKeyframe(float time, Sprite sprite)

    ObjectReferenceKeyframe keyframe = new ObjectReferenceKeyframe();
    keyframe.time = time / FRAMERATE;
    keyframe.value = sprite;
    return keyframe;

现在,曲线的问题,我想我设法解决了,用这段代码替换它,用 AnimationCurve 替换 EditorCurveBinding

AnimationClip clip = ...
AnimationCurve curve = new AnimationCurve();
clip.SetCurve(path, typeof(SpriteRenderer), "m_Sprite", curve);

但我不知道如何为每个动画设置精灵。我认为使用curve.AddKey可能会有所帮助,但我没有看到在那里添加精灵。

如何重写该代码以避免使用UnityEditor

Full code

【问题讨论】:

如果动画对于一类设备(例如剑)总是相同的,为什么不创建一个包含动画的父对象,然后将特定剑添加为子对象?这将使动画与齿轮分离,您只需要创建一组有限的动画。 您的意思是为每个齿轮创建单独的动画,并将动画分散到不同的游戏对象上?我理解正确吗? 不是针对每个单独的装备,而是针对每个类别的装备。例如一个动画用于所有剑,一个用于所有长矛,一个用于所有盾牌等。然后您可以拥有一个没有任何图形处理动作的游戏对象,并在小时候附加齿轮的实际图形/统计/逻辑。 问题在于动画师。状态机一次只能控制一个动画。它无法为每一类齿轮控制单独的动画。 为什么不让每个装备一个动画师,然后在你的播放器脚本中使用参数触发它?否则无论如何你都必须处理多个并发动画,例如同时砍杀和行走。 【参考方案1】:

就我个人而言,我会完全避免使用内置的 Animator 和 Animations,因为该工具的用途非常狭窄,即以预定义的方式为单个对象设置动画(例如:用于过场动画)。

题外话 - 性能

除此之外,玩家还可以选择在游戏中切换到不同的盔甲武器组合。最后为了解决这个问题,我使用了以下对象结构。

您可能知道这在内存方面非常低效,并且会降低性能(禁用的对象仍然有边际 CPU 开销)。并且会增加新对象的加载时间和实例化时间。

我可以使用 Animator 或 Animation 吗?

因为 Animator 没有 API 来访问它的内部结构和动画类型,总的来说,除了告诉 Play or Stop 之外,你不能对它做任何事情。 由于我了解您的动画是“基于 Sprite”而不是“基于变换”的,因此任何理智的想法都是低效的!

我发誓反对的最佳解决方案如下:

创建可以“动画化”的“触发点” 基于触发点 - 改变精灵
    public class AnimationController : MonoBehaviour
    
        public int AnimationIndex;
        public Sprite[] AnimationSprites;
        public SpriteRenderer SpriteRenderer;
        
        private void Update()
        
            SpriteRenderer.sprite = AnimationSprites[AnimationIndex];
        
    

更好的解决方案?

由于我们已经需要自定义结构来管理我们的 sprite,我们可能想要优化整个事情,因为我们几乎没有使用 Animator 的任何功能,我们可以编写自定义控制器来替换这种情况下的 Animator。这应该会显着提高性能,因为 Animator 非常重!

    // MonoBehaviour is optional here
    public class SpriteRendererAnimationHandler
    
        // More fields that would control the animation timing
        
        public Sprite[] AnimationSprites;
        public SpriteRenderer SpriteRenderer;
        
        public void OnAnimationUpdate(int index)
        
            var resolvedIndex = ResolveIndex(index);
            SpriteRenderer.sprite = AnimationSprites[resolvedIndex];
        

        private int ResolveIndex(int index)
        
            // Resolve animation index to sprite array index
        
    

    // One controller per character - or global per game that synchronize all animations to locked FPS 12.
    public class AnimationController : MonoBehaviour
    
        private List<SpriteRendererAnimationHandler> Handlers = new List<SpriteRendererAnimationHandler>();

        public void FixedUpdate()
        
            foreach (var handler in Handlers)
            
                // Calculate animation index
                int calculatedAnimationIndex = ...;
                handler.OnAnimationUpdate(calculatedAnimationIndex);
            
        
    

【讨论】:

构建我自己的动画系统似乎真的有点矫枉过正!没有其他现有的可以使用吗? 第一个解决方案是您可以与现有动画系统一起使用的最佳解决方案,该系统是一个自定义类,用于存储您的图像并“动画”索引。否则,正如您提到的,您将需要数千个可以通过编辑器脚本生成的动画。 但这是否意味着我必须在每个关键帧上设置动画事件? 我不知道我是否正确理解了你的问题。您不必在每个关键帧上都设置动画事件。当精灵需要更改时,您需要经常制作动画事件。正如您目前使用精灵一样,但使用索引。例如:如果在你的攻击动画中盾牌总是相同的精灵,但与空闲动画不同,那么你需要在动画开始时设置索引(精灵)。 "您不必在每个关键帧上都设置动画事件。当精灵需要更改时,您需要经常制作动画事件"

以上是关于在 Unity 中以编程方式创建动画?的主要内容,如果未能解决你的问题,请参考以下文章

多个ListViews在android中以编程方式创建

在 Swift 中以编程方式创建 NSTextField

在android中以编程方式创建布局 - 问题

在 iPhone 中以编程方式放大和缩小:如何设置动画速度?

在 Keycloak 中以编程方式创建客户端

在 Swift 中以编程方式创建 UITableViewController