When implementing footsteps in Unity, there are according to game genre and style (2D, FPS, etc.) different techniques we can use. Depending on whether the game uses animations to “move” the player, or if the game moves the player through physics alone, we can choose one or the other method. The question arises as to whether we should use loops that we start and stop while “walking”, or whether we should use individual samples for each step. Theoretically, both options are possible and it really depends on how the game was programmed.
In this tutorial I’ll show you how to implement footsteps with and without Animation Events.
Implementing Footsteps with Animation Events in Unity
If the game has animation in form of animation clips, then we can use animation events to implement sounds. These are quite useful when it comes to syncing audio to animations.
How do Animation Events work?
Animation events allow us to call any method on a specific keyframe. The script with the method must be located in the same GameObject that contains the Animator component.
Preparation in FMOD Studio
In FMOD Studio, we need an event for the footsteps that has an empty timeline and a game parameter with a name of your choice, in my case “Terrain”. After all, we want to play different sounds for different floor types:
The labeled parameter Terrain contains 4 entries, namely Grass, Gravel, Wood and Water. We place a multi-instrument that contains individual samples for the terrain type at each of these values. If you only want one terrain type, you can omit the game parameter completely and move the multi-instrument to the timeline.
Preparation in Unity
In Unity we have to define additional layers and assign them to our ground GameObjects. We go to Edit -> Project Settings -> Tags & Layers and create new layers:
Water is already a Builtin layer. We simply use this layer for our purposes. Then we go through the ground GameObjects and assign the corresponding correct layer in the upper right corner of the inspector:
C# code for playing footsteps events in Unity
After the preparations we select the GameObject in which the Animator component responsible for the player animations is located. There we create a script for the footsteps sounds. We first declare an enumerator for the different ground types and of course also the FMOD Event Instance:
private enum CURRENT_TERRAIN { GRASS, GRAVEL, WOOD_FLOOR, WATER };
[SerializeField]
private CURRENT_TERRAIN currentTerrain;
private FMOD.Studio.EventInstance foosteps;
We then create the DetermineTerrain
method, which will help us to identify he correct ground type the player is currently walking on:
private void DetermineTerrain()
{
RaycastHit[] hit;
hit = Physics.RaycastAll(transform.position, Vector3.down, 10.0f);
foreach (RaycastHit rayhit in hit)
{
if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Gravel"))
{
currentTerrain = CURRENT_TERRAIN.GRAVEL;
}
else if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Wood"))
{
currentTerrain = CURRENT_TERRAIN.WOOD_FLOOR;
}
else if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Grass"))
{
currentTerrain = CURRENT_TERRAIN.GRASS;
}
else if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Water"))
{
currentTerrain = CURRENT_TERRAIN.WATER;
}
}
}
Basically we send a ray from the player position to Vector3.down with a distance of 10 game units. In the if statements we check if the ray hits a GameObject with the respective layer. If so, we change currentTerrain
. We put this method into Unity’s Update() method.
Now we’ll take care of playing the footstep sounds. We create a very simple PlayFootsteps()
method:
private void PlayFootstep(int terrain)
{
foosteps = FMODUnity.RuntimeManager.CreateInstance("event:/Footsteps");
foosteps.setParameterByName("Terrain", terrain);
foosteps.set3DAttributes(FMODUnity.RuntimeUtils.To3DAttributes(gameObject));
foosteps.start();
foosteps.release();
}
The method takes an int argument that is used to set the “Terrain” game parameter. We specify this variable in another method:
public void SelectAndPlayFootstep()
{
switch (currentTerrain)
{
case CURRENT_TERRAIN.GRAVEL:
PlayFootstep(1);
break;
case CURRENT_TERRAIN.GRASS:
PlayFootstep(0);
break;
case CURRENT_TERRAIN.WOOD_FLOOR:
PlayFootstep(2);
break;
case CURRENT_TERRAIN.WATER:
PlayFootstep(3);
break;
default:
PlayFootstep(0);
break;
}
}
With this switch case we finally play a footstep sound. But how do we know that the int values correspond to certain ground types? In FMOD Studio, these values are displayed to the right of the label in the game parameter configuration:
The complete script for the footsteps now looks like this:
public class PlayerFootsteps : MonoBehaviour {
private enum CURRENT_TERRAIN { GRASS, GRAVEL, WOOD_FLOOR, WATER };
[SerializeField]
private CURRENT_TERRAIN currentTerrain;
private FMOD.Studio.EventInstance foosteps;
private void Update()
{
DetermineTerrain();
}
private void DetermineTerrain()
{
RaycastHit[] hit;
hit = Physics.RaycastAll(transform.position, Vector3.down, 10.0f);
foreach (RaycastHit rayhit in hit)
{
if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Gravel"))
{
currentTerrain = CURRENT_TERRAIN.GRAVEL;
break;
}
else if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Wood"))
{
currentTerrain = CURRENT_TERRAIN.WOOD_FLOOR;
break;
}
else if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Grass"))
{
currentTerrain = CURRENT_TERRAIN.GRASS;
}
else if (rayhit.transform.gameObject.layer == LayerMask.NameToLayer("Water"))
{
currentTerrain = CURRENT_TERRAIN.WATER;
}
}
}
public void SelectAndPlayFootstep()
{
switch (currentTerrain)
{
case CURRENT_TERRAIN.GRAVEL:
PlayFootstep(1);
break;
case CURRENT_TERRAIN.GRASS:
PlayFootstep(0);
break;
case CURRENT_TERRAIN.WOOD_FLOOR:
PlayFootstep(2);
break;
case CURRENT_TERRAIN.WATER:
PlayFootstep(3);
break;
default:
PlayFootstep(0);
break;
}
}
private void PlayFootstep(int terrain)
{
foosteps = FMODUnity.RuntimeManager.CreateInstance("event:/Footsteps");
foosteps.setParameterByName("Terrain", terrain);
foosteps.set3DAttributes(FMODUnity.RuntimeUtils.To3DAttributes(gameObject));
foosteps.start();
foosteps.release();
}
}
Add footsteps sounds to animations using Animation Events
Now open the Animation window, click the Player-GameObject (or a Child GameObject that contains the Animator component), and then select one of the walk or run animations from the Animation Clip drop-down list:
We right click below the timeline and above the keyframes on the free dark grey space. Select the Create Animation Event option. As a result, a small button/arrow (the Animation Event) should have been placed at the desired position/keyframe. In the inspector we now see that we can choose between different methods from a drop-down list:
There we select the method SelectAndPlayFootstep()
and repeat the process one more time. It is best to always select the keyframes where the player places the foot on the floor. In some situations, where this may not be quite clear, it is also sufficient if the two animation events are at the same distance from each other. It looks like that to me now:
Repeat this process for more animation clips. If you’ve done everything right, you should be able to hear your footsteps in Play Mode.
Implement footsteps without animations in Unity
We can also implement footsteps without animation clips for movements. In theory, we create a timer that plays a footstep sound when the player moves at a specific time we choose.
In practice, we declare the PlayerController, create a float variable for the timer and a float variable for the repetition rate of the footsteps:
private PlayerController playerController;
float timer = 0.0f;
[SerializeField]
float footstepSpeed = 0.3f;
In Unitys Awake() method, we access the PlayerController:
private void Awake()
{
playerController = GetComponentInParent<PlayerController>();
}
We then use the Update() method to check whether the player is moving and standing on the floor. We add the timer with Time.deltaTime
. At the same time we check if the timer has reached our fixed footstepSpeed
variable, play a footstep sound and reset the timer:
private void Update()
{
DetermineTerrain();
if (playerController.IsWalking && playerController.IsGrounded)
{
if (timer > footstepSpeed)
{
SelectAndPlayFootstep();
timer = 0.0f;
}
timer += Time.deltaTime;
}
}
If we want to play the footsteps faster, we can change the footstepSpeed variable and make it smaller. In the PlayerController the boolean variables IsWalking and IsGrounded have already been pre-programmed. Ask your developer if he can help you. You’re also welcome to use the implementation in the PlayerController example .