Feeding Unity's Animator

An effort to reduce dependency hell

·

4 min read

For my special prototype I have decided to reduce script dependencies as much as possible and try to embrace the Component's Pattern around which Unity is built as much as possible.

AnimatorFeeder is a simple MonoBehaviour Script component whose job is, surprisingly, to feed an Animator with values coming from gameplay. Gameplay scripts operate by determining the value of special ScriptableObject variables, following the idea presented by Ryan Hipple at Unite Austin 2017. So an InputReader script could set the magnitude of the input Vector as a FloatVariable. This variable can then be referenced directly by other components, without having to access the owning script to get its value. The end goal is to try and decoupling scripts as much as possible because if there is a hell I've been used to in Unity is that made of spaghetti GetComponent()s.

Screenshot 2021-03-14 at 22.17.17.png

As you can see, the AnimatorFeeder exposes a set of parameter mappings to the inspector, responsible to map variable values to actual Animator parameters. Wiring them correctly is up to the designer.

Once set up, the rest is very simple. On every update tick, these mappings are scanned and the corresponding variable values are sent to the Animator, triggering the appropriate state.

[Header("Variable Mappings")]
public FloatVariableAnimatorMap[] floatVariableMaps;
public BoolVariableAnimatorMap[] boolVariableMaps;
public IntegerVariableAnimatorMap[] intVariableMaps;

[...]

private void Update()
        {
            foreach (var floatMap in floatVariableMaps)
            {
                animator.SetFloat(floatMap.targetAnimParam, floatMap.variable.value);
            }

            foreach (var boolMap in boolVariableMaps)
            {
                animator.SetBool(boolMap.targetAnimParam, boolMap.variable.value);
            }

            foreach (var intMap in intVariableMaps)
            {
                animator.SetInteger(intMap.targetAnimParam, intMap.variable.value);
            }
        }

Unity does unfortunately not offer Dictionary serialization out of the box so, in order for the mappings to be designer friendly (and given the problems that hooking into the serialization process might still suffer from), I decided to go with a simple yet quite effective approach: each mapping type is its own Serializable struct:

    [Serializable]
    public struct FloatVariableAnimatorMap
    {
        public FloatVariable variable;
        public string targetAnimParam;
    }

    [Serializable]
    public struct BoolVariableAnimatorMap
    {
        public BoolVariable variable;
        public string targetAnimParam;
    }

    [Serializable]
    public struct IntegerVariableAnimatorMap
    {
        public IntegerVariable variable;
        public string targetAnimParam;
    }

As far as I could understand by researching the topic, it doesn't come at any burdensome cost.

And that's that. The component I came up with keeps dependencies to the minimum and tries to do one thing only and hopefully this will yield some results in terms of less headaches down the road.

PS: An optimization I highly recommend and that I plan to introduce very soon is to hash the Animator Parameters inside OnEnable() to gain some performance points.