Footsteps Audio System in Unreal Engine and FMOD (Blueprints and C++)
Si prefieres leer una versión en Español, da click en este botón =>
Unreal Engine Version: 5.0.3
FMOD Version: 2.02.08
This blog post shows you how to create a footstep audio system in Unreal Engine 5 and FMOD. This system uses a line trace spawned from the Player Character's location down to the floor. This line trace will gather information about the Physical Material of the surfaces and set FMOD parameters to change the type of footstep sounds.
I implemented the same system twice for this test project, one using Blueprints visual scripting and another using native C++. Here is the list of essential tools and keywords used for this project:
I programmed all the functionality inside the MyUE5ProjectCharacter C++ parent class, the BP_ThirdPersonCharacter child blueprint class, and the animation blueprint ABP_Manny.
Important:
Please consider that this guide does not intend to teach creative sound design. Instead, it exclusively focuses on game audio's implementation and programming aspects.
Download the project here:
IMPORTANT!
〰️
IMPORTANT! 〰️
Prerequisites:
Create an empty C++ third-person project called “MyUE5Project”.
Download and integrate FMOD’s plugin into your project.
Create a new FMOD Studio project and set your bank’s output directory to MyUEProject/Content/FMOD
Build your Master Bank
For this tutorial to make sense, you need to know how to integrate the FMOD plugin and call its properties in Unreal Engine. Additionally, consider reading and referencing my previous post about UPROPERTY & UFUNCTION:
IMPORTANT!
〰️
IMPORTANT! 〰️
FMOD Setup:
Inside the FMOD project, create a 3D Timeline Event called Play_Footsteps. Add four tracks and a Multi Instrument on each of them. Import your audio files inside each Multi Instrument.
Go to Window > Preset Browser, or press Command - 8 on your keyboard and then click on the New Parameter button. Create a Labeled Parameter called “Footsteps” with four labels, one per instrument. Set the first one as the initial value. Finally, check the Hold Value During Playback option to ensure that the sounds won't be interrupted if the parameter changes during playback.
Unfortunately, Unreal Engine doesn't support calling FMOD parameters by label names, but we can use the associated number values to achieve the same result.
Select a Multi Instrument, and go to FMOD's Deck at the bottom of the editor. Click on the Trigger Behaviour arrow and then Add Condition. Select "Footsteps" and finally select the appropriate Label. As an additional step, you can set a random pitch modulation by right-clicking on Pitch > Add Modulation. This setting adds further variation to the footstep sounds. Do this process for every Multi Instrument on the event.
Save and build the Master Audio Bank.
Materials and Physical Materials:
Environmental Artists use Material and Physical Material assets to define the look and physics simulation of meshes in the game. As game audio artists and programmers, we can use these materials attached to surfaces and a Line trace to set FMOD parameters dynamically.
Create a Materials folder and add a Material and a Physical Material per each required surface. For this project, I created four different materials.
Double click on each material and set its color and Physical Material on the editor.
Go to Window > Place Actors > Plane, drag and position these new surfaces in the viewport, and then their Material. Additionally, you can add a name to them with a Text Render Component.
C++ Setup:
I implemented this project's properties and a FMOD Audio Component in C++.
The blueprint class will inherit all these properties from the parent and expose them in the Blueprint graph. Finally, I created two functions in C++ and two functions in Blueprints that do the same to show you how both systems mirror each other.
Important:
I use Doxygen style comments to explain the code. These comments won’t compromise the code's functionality or compilation. Feel free to copy the code as it’s written here.
Build.cs:
Open the solution in your IDE and locate MyUE5Project.Build.cs in MyUE5Project > Source, and add "FMODStudio" and "PhysicsCore" as dependencies to the build.
/** Add "FMODStudio" and "PhysicsCore" to be able to use and compile FMOD's API and UPhysicalMaterial in C++ */ PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "FMODStudio", "PhysicsCore"});
Property and Function Declarations:
Go to MyUE5Project > Source, and open MyUE5ProjectCharacter.h. Include these directives:
.h file:
#include "FMODBlueprintStatics.h" // Include this directive to access FMOD API. #include "PhysicalMaterials/PhysicalMaterial.h" // Include this directive to work with Physical Materials.
Declare an FMOD Audio Component, a TMap, a boolean, and a float.
.h file:
/** Fmod Audio Component. Created in the class constructor. */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Audio") UFMODAudioComponent* FmodAudioComponent; /** A map data structure. Holds Physical Materials as keys and floats as values. * This values will set the correct parameter number in FMOD */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Audio") TMap<UPhysicalMaterial*, float> PhysicalMaterialMap; /** Boolean property that sets AddOnScreenDebugMessage function on and makes the line trace visible in game. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Audio") bool bPrintDebug; /** Defines how long the line trace extends from our Character's location on the Z vector. * Positive numbers up vector, negative numbers down vector. -150cm set as default */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Audio") float Offset = -150;
Declare two functions, GetPhysicalMaterialByLineTrace() and SetFootstepsParameter().
.h file:
/** * @brief Gets a reference of the floor's Physical Material with a Line trace. * @param OffsetZ How far the Line trace extends from our character's center. * @param bDebug Sets visibility for the Line trace. true: Visible, false: Not Visible * @return A Physical Material reference */ UFUNCTION(BlueprintCallable, Category="Audio") UPhysicalMaterial* GetPhysicalMaterialByLineTrace(const float &OffsetZ, const bool &bDebug); /** * @brief Gets a Physical Material reference and sets the appropriate parameter value in FMOD. * @param HitPhysicalMaterial A Physical Material Reference * @param bDebug Sets visibility for printing the current Physical Material to the screen. true: Visible, false: Not Visible * @param ParameterName FMOD parameter name. */ UFUNCTION(BlueprintCallable, Category="Audio") void SetFootstepsParameter(const UPhysicalMaterial* HitPhysicalMaterial, const bool &bDebug, const FName ParameterName);
C++ and Blueprint Implementation:
In this section, I show you how to access the Line trace in the Kismet System Library and how to create an FMOD Audio Component. Finally, I show you the BP_ThirdPersonCharacter implementation in the blueprint graph and then the C++ implementation in the MyUE5ProjectCharacter.cpp file.
.cpp file:
#include "Kismet/KismetSystemLibrary.h" // Include this directive to access the Line Trace functions.
FMOD Audio Component in the Class Constructor:
Create an FMOD Audio Component in the class constructor. The constructor has the same name as the class: MyUE5ProjectCharacter::MyUE5ProjectCharacter(). This FMOD Audio Component will hold the footsteps FMOD event and follow our Character's location in the world. Additionally, this component lets us define parameter values before creating an instance of its event.
.cpp file:
// Create an FMOD Component. FmodAudioComponent = CreateDefaultSubobject<UFMODAudioComponent>(TEXT("Footsteps Audio Component")); //Creates the FMOD Audio Component. FmodAudioComponent->SetupAttachment(GetMesh()); // Attaches the component to the Skeletal Mesh so the sound travels with our Character. FmodAudioComponent->SetAutoActivate(false); // Initializes this component deactivated so it doesn't play at start.
Get Physical Material By LineTrace
This function creates a line trace and returns the Physical Material it hits.
Blueprint:
.cpp file:
UPhysicalMaterial* AMyUE5ProjectCharacter::GetPhysicalMaterialByLineTrace(const float &OffsetZ, const bool &bDebug) { const FVector LineStart = GetActorLocation(); //Sets the Line Start at our Character's location. const FVector LineEnd = FVector(LineStart.X, LineStart.Y, LineStart.Z + OffsetZ); //Sets the Line End at our Character's location plus the offset. EDrawDebugTrace::Type DebugType; // This enumeration is used to set the visibility of the line trace. FHitResult HitResult; // This struct will hold the line trace hit results. if (bDebug) // Activate line trace visibility? DebugType = EDrawDebugTrace::ForDuration; else DebugType = EDrawDebugTrace::None; // Line trace function. UKismetSystemLibrary::LineTraceSingle(this, LineStart, LineEnd,UEngineTypes::ConvertToTraceType(ECC_Visibility), false,TArray<AActor*>(), DebugType, HitResult, true, FLinearColor::Red, FLinearColor::Red, 2.0f); return Cast<UPhysicalMaterial>(HitResult.PhysMaterial); // Cast and return the Physical Material from HitResult. }
Set Footsteps Parameter
This function gets a Physical Material reference and tries to find it as a key in our TMap. If it succeeds, it will set the value related to that key as the current FMOD parameter value. Additionally, it will print the current Physical Material name to the screen.
Blueprint:
.cpp file:
void AMyUE5ProjectCharacter::SetFootstepsParameter(const UPhysicalMaterial* HitPhysicalMaterial, const bool &bDebug, const FName ParameterName) { /* Checks if the map contains the Physical Material as a key before setting the FMOD parameter. * The game will crash if you attempt to find a null reference or a Key that doesn't exist in the map. */ if (PhysicalMaterialMap.Contains(HitPhysicalMaterial)) { FmodAudioComponent->SetParameter(ParameterName, PhysicalMaterialMap[HitPhysicalMaterial]); // Sets the FMOD parameter using the value of the map's key. if (bDebug) // If true, prints the name of the Physical Material to the screen and "C++ Implementation". { GEngine->AddOnScreenDebugMessage(0, 1, FColor::Orange, "Physical Material: " + HitPhysicalMaterial->GetName()); GEngine->AddOnScreenDebugMessage(1, 1, FColor::Red, "C++ Implementation"); } } else // If a valid key wasn't found, sets the default parameter value = 0, otherwise the last set parameter will remain. { FmodAudioComponent->SetParameter(ParameterName, 0); } }
Full Blueprint Graph:
.cpp file:
Save your code and compile it in the editor.
You should be able to see the FMOD Audio Component and all the properties in the components and variables tabs in the BP_ThirdPersonCharacter class. Assign your footsteps event to this component.
The Animation Blueprint:
In the BP_ThirdPersonCharacter, go to the Physical Material Map previously defined in C++ and assign the Physical Materials as keys. Fill in the map's values according to the FMOD parameter values defined in your event.
Right-click on the graph and create a custom event function. I named this function BPF_PlayFootsteps. Finally, drag the Fmod Audio Component to the graph and call its Play function.
All the functionality for dynamically changing FMOD parameters is ready. Now, we need to trigger the footsteps event. To achieve this, implement this functionality in the Character's animation blueprint. Open the Content Drawer, go to Content > Characters > Mannequins > Animations, and open ABP_Manny. Go to the upper right corner of the editor and click on the Animation Sequence button.
In the Asset Browser, open the "MF_Run_Fwd" sequence and add a notify track. Then right-click on it and select Add Notify > New Notify. Call your notify "Footstep." Duplicate your notify on every frame where the skeletal mesh feet hits the floor. Repeat the same process for "MF_Walk_Fwd" and "MM_Walk_InPlace."
Go back to the Animation Blueprint and call the Event Blueprint Begin Play function to cast and save a reference to the BP_ThirdPersonCharacter to a variable once the game begins. Call the notify by searching for the AnimNotify_Footstep function. Finally, call the BPF_PlayFootsteps custom function.
Complete Blueprint Implementation:
I created a branch for this project to select between the blueprint implementation and the native C++ one.