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 usedStepper
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
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.
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.
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.
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
.
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) :
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.