Enrique Moreno Tent
Enrique Moreno Tent

Reputation: 25307

Create animations programmatically in Unity?

In my game I have a big catalog of gear: Armors, weapons and shields. The combinations between these can be really immense.

enter image description here

Besides that, the player has the option of switching in-game to a different set of armor-weapon combination. In the end to solve this, I have used the following object structure.

enter image description here

Whenever I switch the weapons, I activate/deactivate the necessary GameObjects. The animations are set in this way:

enter image description here

Now, the problem is creating the animation. I first considered pre-rendering programatically all the combinations, but my catalog is so huge, that it would create 100s, if not 1000s of animations. So I opted for a different solution. Create in playtime the animation, once I knew what gear would the player select. For that, I created a script to take care of that. The problem is that I have been using APIs from UnityEditor, and now I have realized the build will not work. Specifically because of 2 different classes: EditorCurveBinding and ObjectReferenceKeyframe.

This is a couple snippets of how I was using this classes when creating the animations:

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

Now, the problem with the Curve, I think I managed to solve, replacing it with this code, replacing EditorCurveBinding with AnimationCurve:

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

But I have no idea how to set the sprites for each animation. I thought that using curve.AddKeycould be helpful, but I have seen no way to add a sprite there.

How could I rewrite that code to avoid using UnityEditor?

Full code

Upvotes: 7

Views: 4950

Answers (2)

Graham Hickson
Graham Hickson

Reputation: 1

Add a public field named animation index to Tomasz last example, then create the animations in animator as youd normal do for animation pieces, then animate that animation index field. simple if(currentAnimationyion != lastAnimationIndex) to check if need to send to handlers and ya rocking

Upvotes: 0

Tomasz Juszczak
Tomasz Juszczak

Reputation: 2340

Personally, I would completely avoid built-in Animator and Animations, since this tool is for the very narrow purpose of animating a single object in a predefined way (eg: for a cutscene).

Offtopic - performance

Besides that, the player has the option of switching in-game to a different set of armor-weapon combination. In the end to solve this, I have used the following object structure.

As you probably know this is very inefficient memory-wise and will decrease performance (disabled objects still have marginal CPU overhead). And will incread load time and instantiation time of new objects.

Can I use Animator or Animation?

Because Animator has no API to access it's internals and animation type and overall you cannot do pretty mutch nothing with it except from telling it to Play or Stop. Since I understand that your animation is "Sprite based" and not "Transform based" any sane idea is just inefficient!

Best solution that I vow against is as follows:

  • Create "trigger points" that you would "animate"
  • Based on trigger point - change sprite
    public class AnimationController : MonoBehaviour
    {
        public int AnimationIndex;
        public Sprite[] AnimationSprites;
        public SpriteRenderer SpriteRenderer;
        
        private void Update()
        {
            SpriteRenderer.sprite = AnimationSprites[AnimationIndex];
        }
    }

enter image description here

Better solution?

Since we already need to have custom structure that manages our sprites we might want to optimize the whole thing, since we are not using almost any of the features of Animator we could write custom controller that would replace Animator in this case. This should improve performance significantly since Animator is very heavy!

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

Upvotes: 5

Related Questions