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

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

enter image description here

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

enter image description here

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

enter image description here

现在的问题是创建动画。我最初考虑通过程序预渲染所有组合,但我的目录非常庞大,这将创建数百甚至数千个动画。因此,我选择了另一种解决方案。一旦我知道玩家会选择什么齿轮,就在播放时间内创建动画。为此,我创建了一个脚本来处理它。问题是我一直在使用UnityEditor的API,现在我已经意识到构建将无法工作。具体来说,由于两个不同的类:EditorCurveBindingObjectReferenceKeyframe

以下是我创建动画时如何使用这些类的几个片段:

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;
}

现在,我认为我已经解决了曲线的问题,用这段代码来替换它,将EditorCurveBinding替换为AnimationCurve

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

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

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

完整代码


3
如果一类装备(例如剑)的动画始终相同,为什么不创建一个包含动画的父对象,然后将具体的剑作为子对象添加?这样可以将动画与装备解耦,只需要创建有限的一组动画即可。 - CShark
1
不是为每个单独的装备,而是为每个装备类别创建动画。例如,所有剑共用一个动画,所有长矛共用一个动画,所有盾牌共用一个动画等等。然后,您可以创建一个没有任何图形处理运动的游戏对象,并将实际的图形/统计数据/装备逻辑作为其子对象添加进去。 - CShark
1
问题出在动画师身上。状态机一次只能控制一个动画。它无法为每个齿轮类别控制单独的动画。 - Enrique Moreno Tent
我曾经做过一个类似的项目。起初使用Unity FSM动画,但很快转而使用Spine。使用Spine,你可以为角色及其装备添加动画效果,在运行时还可以替换指定的贴图。如果你考虑使用Spine,我可以让你访问一个演示项目。 - Iggy
@EnriqueMorenoTent 当然,Spine 允许你制作像 Unity 的 Transform 组件一样的点骨骼,具有位置、旋转和缩放功能。 - Iggy
显示剩余4条评论
2个回答

5

个人建议完全避免使用内置的Animator和Animations,因为这个工具只是为了在预定义的方式下动画化单个对象(例如:用于过场动画)。

题外话 - 性能

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

正如您可能知道的那样,这种做法在内存方面非常低效,并会降低性能(禁用的对象仍然具有较小的CPU开销)。并且会增加新对象的加载时间和实例化时间。

我可以使用Animator或Animation吗?

由于Animator没有API可用于访问其内部和动画类型,而且总体上你几乎不能对它做任何事情,除了告诉它播放或停止。 由于我理解你的动画是基于"Sprite"而不是"Transform"的,任何明智的想法都是低效的!

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

  • 创建“触发点”,您可以“激活”
  • 根据触发点-更改精灵
    public class AnimationController : MonoBehaviour
    {
        public int AnimationIndex;
        public Sprite[] AnimationSprites;
        public SpriteRenderer SpriteRenderer;
        
        private void Update()
        {
            SpriteRenderer.sprite = AnimationSprites[AnimationIndex];
        }
    }

enter image description here

更好的解决方案?

既然我们已经需要拥有管理精灵的自定义结构,我们可能希望优化整个过程,因为我们几乎没有使用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);
            }
        }
    }

1
构建自己的动画系统似乎有些过度设计了!难道没有其他现有的可以使用的吗? - Enrique Moreno Tent
第一种解决方案是最好的,您可以使用现有的动画系统,这是一个自定义类,存储您的图像,并“动画化”索引。否则,正如您所提到的,您需要通过编辑器脚本生成数千个动画。 - Tomasz Juszczak
但这难道不意味着我必须在每个关键帧上设置一个动画事件吗? - Enrique Moreno Tent
我不确定我是否正确理解了你的问题。你不需要在每个关键帧上设置动画事件。当精灵需要更改时,您需要定期创建动画事件。就像您目前使用的精灵一样,但是使用索引代替。例如:如果在攻击动画中盾牌始终具有相同的精灵,但与空闲动画不同,则需要在动画开始时设置索引(精灵)。 - Tomasz Juszczak
1
不需要在每个关键帧上设置动画事件。只需在精灵需要更改时定期创建动画事件。关键帧就是那个...精灵需要改变的时刻。 - Enrique Moreno Tent

0
在Tomasz的最后一个示例中添加一个名为animation index的公共字段,然后像平常一样在动画器中创建动画片段,然后对该animation index字段进行动画处理。简单地使用if(currentAnimationyion != lastAnimationIndex)检查是否需要发送到处理程序,然后就可以愉快地玩耍了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接