Show / Hide Table of Contents

Setup Resolvers in the Surface

As explained in Concepts/Behaviour and the Resolving Assets, Surface asset is the most important because it holds the resolver that would finally let the library know the resulting FootstepBehaviour to play.

Creating assets for required combinations

Plan out what you need for your game and create some of 4 resolving assets (everything other than FootstepBehaviour). Name them according to your use case.

Here is a recap from Concepts/Behaviour and the Resolving Assets :

  • Surface : Intended to represent different surfaces.
  • SurfaceModifer : Intended to represent different conditions of the surface.
  • Stepper : Intended to represent footwears, or actor that is stepping (such as what kind of animal).
  • StepperModifier : Intended to represent different actions performed on the surface, or also footwears if you used Stepper as something else such as character's name.

Then create as many FootstepBehaviour as much as combinations of all those assets. Multiply them all together to get the required number. You can name them as concatenation of all 4 assets that resolves into it.

You don't have to touch any of these assets yet, just create for GUID identity for linking them up together for now. Let's look at some examples.

How to create any Modular Footstep asset

Creating assets

Right click inside Project panel, then select Create > Modular Footstep > ___. You will see a menu to create all 5 kinds of asset : Surface, SurfaceModifier, Stepper, StepperModifier, and FootstepBehaviour, as explained in Concepts/Behaviour and the Resolving Assets.

Two types of resolver

Resolvers are inside a Surface asset. It is a menu that specify what FootstepBehaviour you would ended up getting when you provided the other 3 : SurfaceModifier + Stepper + StepperModifier. (Surface is already provided, since the resolver is already in a Surface.)

You can choose from tree or array style to program the resolver inside Surface asset. To see which one suits you better let's look at this example setup :

(Surface) A
(SurfaceModifier) J
(SurfaceModifier) K
(Stepper) P
(Stepper) Q
(StepperModifier) X
(StepperModifier) Y
(StepperModifier) Z

Which would results in the following 12 unique FootstepBehaviour, because 1*2*2*3 = 12 :

AJPX, AJPY, AJPZ
AJQX, AJQY, AJQZ

AKPX, AKPY, AKPZ
AKQX, AKQY, AKQZ

You inspect on Surface asset A to access these resolvers inside it :

The array resolver

In array (linear) resolver, you would have to specify the following 12 menus in the resolver explicitly :

A + J + P + X = AJPX
A + J + P + Y = AJPY
A + J + P + Z = AJPZ

A + J + Q + X = AJQX
A + J + Q + Y = AJQY
A + J + Q + Z = AJQZ

A + K + P + X = AKPX
A + K + P + Y = AKPY
A + K + P + Z = AKPZ

A + K + Q + X = AKQX
A + K + Q + Y = AKQY
A + K + Q + Z = AKQZ

The library finds the right formula in the menu by linearly searching through this array.

On an actual UI, it looks like this.

Array resolver

Tip

While Modular Footstep is supported from 2019.4 LTS or higher, to get a nice array drawer with rearrange handle and + - buttons like in this image, you need at least 2020.3 LTS.

A is not involved in any fomula in this image, because the resolver is already in Surface asset A. It is already implied that it is the first ingredient for all fomula to resolve into FootstepBehaviour.

While straightforward and easy to understand, notice that you need to repeat J muliple times for each menu that use it. Everything is linear. This can be hard to setup and also manage (e.g. change later, add more) when you have got more of the outer components. But this format is ideal for simpler games.

Modular Footstep have an another kind of resolver to help you organize better.

The tree resolver

The tree resolver is opinionated to resolve in this order :

SurfaceModifier -> Stepper -> StepperModifier

This is an actual UI, which may looks intimidating, but you can work on part of the tree, unfolding only that part.

Tree resolver

An advantage is that the further inside the tree, the less you have to repeat yourself about the ingredient of that combination. Notice that the innermost X Y Z is implied to be inside P or Q, and also inside J or K, because we had already travelled past them in the tree. This tree has the same resolving effect as the array one. Choose the one you like.

Example plans

Example 1

A simple game with fixed jump, dash, landing sound effects. These are played normally with audioSource.PlayOneShot.

The walk sound is also fixed regardless of surface, but you want the convenience of calling start-stop of Interval Play and programming/randomized clip picking capability with FootstepBehaviour, so only for walking, you would like to use Modular Footstep.

In this case, we need :

(Surface) AnySurface.asset

(FootstepBehaviour) AnySurface-Walk.asset

Then program AnySurface so that the resolver resolves null + null + null (SurfaceModifier, Stepper, StepperModifier, respectively) into AnySurface-Walk.

In this case, array resolver is clearly better than tree style because you only have 1 combination. It is more to the point.

Example 1

You can also use explicit assets in place of null, like this :

(Surface) AnySurface.asset
(SurfaceModifier) NeutralSurface.asset
(Stepper) AnyCharacter.asset
(StepperModifier) NormalFoot.asset

(FootstepBehaviour) AnySurface-Walk.asset

Then program AnySurface so that the resolver resolves NeutralSurface + AnyCharacter + NormalFoot into AnySurface-Walk.

Example 1 (2)

A note on null, if you have only that explicit menu like in this image, inputting null + null + null will not resolve into AnySurface-Walk. null is its own thing rather than defaulting into something. (There is a fallback mechanic when it can't find the right combination inside Surface : Advanced/Tree Resolver Fallback)

Example 2

You have a game with 2 selectable characters (male and female). The characters cannot be changed in appearance, and therefore the footwear doesn't change for an entire game. The female character wears high heel, so it make sense that selecting this character makes completely different footstep sound.

The same with the previous example, there is no distinction of surface in the game. But now you want some kind of organizational tool from Modular Footstep to also handle jumping and landing as well as the walk.

If there are 2 characters (male, female) with 3 actions (walk, jump, land), you need these assets. Assuming you use null for SurfaceModifier to skip it :

(Surface) AnySurface.asset
(Stepper) MaleCharacter.asset
(Stepper) FemaleCharacter.asset
(StepperModifier) Action-Walk.asset
(StepperModifier) Action-Jump.asset
(StepperModifier) Action-Land.asset

(FootstepBehaviour) Male-Walk.asset
(FootstepBehaviour) Male-Jump.asset
(FootstepBehaviour) Male-Land.asset
(FootstepBehaviour) Female-Walk.asset
(FootstepBehaviour) Female-Jump.asset
(FootstepBehaviour) Female-Land.asset

You may think that you have to create 6 FootstepBehaviour "anyway", in addition to having to create similar amount of Surface, Stepper and StepperModifier. It looks like additional workload for no reason!

But remember that the API takes in Surface + SurfaceModifier + Stepper + StepperModifier, not a single FootstepBehaviour. It gives you modularity to, for example, switch around only Stepper according to the current character and have everything else the same, for it to resolve correctly into the right FootstepBehaviour.

If an API only takes FootstepBehaviour linearly, you would have to program C# anyways which one of the 6 FootstepBehaviour to use according to various situations, and then you have just reinvented the wheel how my resolver works with if else or switch case.

I think we should setup the tree resolver AnySurface this time instead of array. It would looks like this :

(Surface) AnySurface.asset/
└── (SurfaceModifier) null/
    ├── (Stepper) MaleCharacter.asset/
    │   ├── (StepperModifier) Action-Walk.asset/
    │   │   └── resolves into --> (FootstepBehaviour) Male-Walk.asset
    │   ├── (StepperModifier) Action-Jump.asset/
    │   │   └── resolves into --> (FootstepBehaviour) Male-Jump.asset
    │   └── (StepperModifier) Action-Land.asset/
    │       └── resolves into --> (FootstepBehaviour) Male-Land.asset
    └── (Stepper) FemaleCharacter.asset/
        ├── (StepperModifier) Action-Walk.asset/
        │   └── resolves into --> (FootstepBehaviour) Female-Walk.asset
        ├── (StepperModifier) Action-Jump.asset/
        │   └── resolves into --> (FootstepBehaviour) Female-Jump.asset
        └── (StepperModifier) Action-Land.asset/
            └── resolves into --> (FootstepBehaviour) Female-Land.asset

On a slightly intimidating UI, it looks like this (cropped) :

Example 2

If the game has more kinds of Surface, you would repeat doing this in an another Surface asset. This massive tree being in different assets will make it more managable.

Tip

You can use both array and tree resolvers at the same time. It will resolve the tree first then the array. You can use this creatively, such as if your game has a special one-off character that the player can control in only a specific stage. You can setup resolving menu for this character in the array resolver as a special case because there are limited terrain types in that stage.

In This Article
Back to top
A Unity plugin by 5argon from Exceed7 Experiments. Problems/suggestions/contact : 5argon@exceed7.com Discord Unity Forum