Spatialization #1 in Unreal Engine and Wwise (Blueprints and C++)
Si prefieres leer una versión en Español, da click en este botón =>
Unreal Engine Version: 5.0.3
Wwise Version: 2021.1.10.7883
This blog post shows you four different audio spatialization techniques in Unreal Engine 5 and Wwise. In this post I show how to implement a 3D Sound, a Cone Attenuated Sound, a Multi-Position Sound, and a sound that follows the listener using an Spline Component. All these sounds will be triggered using the OnComponentBeginOverlap() function of the actor's Sphere Component. Additionally, this project offers a good example on how inheritance works between C++ classes and Blueprint classes in Unreal Engine 5.
I implemented the same systems 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 SoundSourceActor C++ parent class, BP_SoundSourceActor, BP_RotationSoundSourceActor, Bp_MultiPosSoundSourceActor, and BP_SplineSoundSourceActor Blueprint classes.
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++ first-person project called “MyUE5Project”.
Download and integrate the Wwise plugin into your project through the Wwise Launcher.
Activate Event-Based Packaging
For this tutorial to make sense, you need to know how to integrate the Wwise plugin into Unreal Engine. Additionally, consider reading and referencing my previous post about UPROPERTY & UFUNCTION:
IMPORTANT!
〰️
IMPORTANT! 〰️
Wwise Setup:
Inside the Wwise project, I created four events. Each event plays a blend container that has an spatialized sound, and a non-spatialized Voice-Over narration.
Each blend container implements different positioning settings, including different attenuation ShareSets.
Project's Viewport:
Each sound is emitted by an Ak Component inside a Blueprint Actor inherited from a SoundSourceActor C++ class. Each actor has an Sphere Component that triggers the sound and also shows the distance attenuation range.
C++ Setup:
I created a SoundSourceActor C++ parent class with all the Components and functions required for all the child classes. These properties and functions are exposed and implemented in Blueprints.
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:
Inside MyUE5Project.Build.cs in MyUE5Project > Source, I added "AkAudio" as a dependency to the build.
// Add "AkAudio" to use and compile Wwise's API in C++ PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "AkAudio" });
Property and Function Declarations:
In the Content Drawer, I created a new C++ actor class called SoundSourceActor.
Find this class in MyUE5Project > Source > SoundSourceActor.h
These are the included directives:
.h file:
#include "AkComponent.h" // Include this directive to access Ak Component. #include "Components/SphereComponent.h" // Include this directive to access Sphere Component. #include "Components/SplineComponent.h" // Include this directive to access Spline Component.
I declared an Static Mesh Component, an Ak Component, an Sphere Component, an Spline Component, a Player Character, a Boolean, and a FString .
.h file:
/** This actor's static mesh. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") UStaticMeshComponent* StaticMesh; /** This actor's Ak Component - Holds and audio event and allows spatialization. */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Audio") UAkComponent* AkComponent; /** A sphere component to generate overlap events. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") USphereComponent* SphereComponent; /** A spline will allow this actor to follow the player character's location. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") USplineComponent* SplineComponent; /** A reference to the player's character. */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Audio") ACharacter* PlayerCharacter; /** A boolean property to activate debug messages. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") bool bDebug = true; /** The message to display on screen for debug. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") FString DebugMessage;
I declared five functions: PlayAudioEvent(), RotateActorZ(), SetFiveSoundPositions(), SplineFollowPlayerCharacter(), and ClearOnScreenDebugMessages().
.h file:
/** * @brief Plays or stops the associated event in the AkComponent. Prints a debug message. * @param bPlay true = Play | false = Stop. * @param Debug true = Debug | false = No Debug. * @param _DebugMessage Debug message to print. */ UFUNCTION(BlueprintCallable, Category="Audio") void PlayAudioEvent(bool bPlay, bool Debug, const FString& _DebugMessage); /** * @brief Rotates the actor on the Z rotator (Yaw). * @param Delta The time difference between the previous frame and the current frame. * @param DegreesPerSecond How many degrees to rotate this actor each second. */ UFUNCTION(BlueprintCallable, Category="Audio") void RotateActorZ(float Delta, float DegreesPerSecond); /** * @brief Creates five transforms to generate a multi-position sound source. * @param OffsetY1 An offset from the actor's zero vector. * @param OffsetY2 An offset from the actor's zero vector. */ UFUNCTION(BlueprintCallable, Category="Audio") void SetFiveSoundPositions(float OffsetY1, float OffsetY2); /** Follow the Player Character's location along the Spline Component. * Should be called from a Tick function */ UFUNCTION(BlueprintCallable, Category="Audio") void SplineFollowPlayerCharacter(); /** Custom function to clear all debug messages from the screen. * Exposed to blueprints. */ UFUNCTION(BlueprintCallable, Category="Audio") void ClearOnScreenDebugMessages();
Directives and the Constructor in the .cpp file:
In the SoundSourceActor.cpp file, include these directives
.cpp file:
#include "GameFramework/Character.h" // Include this directive to access ACharacter functions. #include "AkGameplayStatics.h" // Include this directive to access Wwise functions. #include "Kismet/GameplayStatics.h" // Include this directive to access the GetPlayerCharacter() function.
Components in the Class Constructor:
I created all the components in the constructor, and defined attachments for them:
Static Mesh is the Root Component
Ak Compoment attached to Static Mesh(Root Component)
Sphere Component attached to Ak Component
Spline Compoment attached to Static Mesh(Root Component)
.cpp file:
// Creates a Static Mesh Component. StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent")); SetRootComponent(StaticMesh); // Sets this Static Mesh as the actor's Root Component. // Creates an Ak Component. AkComponent = CreateDefaultSubobject<UAkComponent>(TEXT("Ak Component")); AkComponent->SetupAttachment(RootComponent); // Attaches this Ak Component to the Root Component. // Creates a Sphere Component. SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); SphereComponent->SetupAttachment(AkComponent); // Attaches this Spline Component to the Ak Component. SphereComponent->SetHiddenInGame(false); // Sets this component visibility in gameplay. // Creates a Spline Component. SplineComponent = CreateDefaultSubobject<USplineComponent>(TEXT("SplineComponent")); SplineComponent->SetupAttachment(RootComponent); // Attaches this Spline Component to the Root Component. SplineComponent->SetAutoActivate(false); // Initializes this component De-activated. Its activated on the "BP_SplineSoundSourceActor" class.
BP_SoundSourceActor (3D Sound)
I created this Blueprint class based on the SoundSourceActor C++ class.
Play Audio Event:
This function starts or stops the Ak Event.
Blueprint:
Inherited from SoundSourceActor.cpp file:
void ASoundSourceActor::PlayAudioEvent(bool bPlay, bool Debug, const FString& _DebugMessage) { if(AkComponent->AkAudioEvent) // Continue if there is an Ak Audio Event assigned. { if(bPlay) // Continue if bPlay = true. { // Play the associated Ak Audio Event. AkComponent->PostAssociatedAkEvent(0, FOnAkPostEventCallback(), TArray<FAkExternalSourceInfo>()); if (Debug) // Prints debug messages if Debug = true. { GEngine->AddOnScreenDebugMessage(1, 60.0f, FColor::Green, _DebugMessage); GEngine->AddOnScreenDebugMessage(2, 60.0f, FColor::Red, "C++ Implementation"); } } else // Stops the Ak Audio Event and clears all Debug messages if bPlay = false. { AkComponent->Stop(); ClearOnScreenDebugMessages(); } } }
.cpp file:
On Component Begin/End Overlap
This functionality is implemented in Blueprints and is inherited by all the children of BP_SoundSourceActor class. When the Player Character overlaps with the Sphere Component, the Ak Event starts. The event stops when the Character exits the sphere's radius.
Blueprint:
BP_RotationSoundSourceActor (Cone Att.)
I created this Blueprint class based on the BP_SoundSourceActor Blueprint class.
Rotate Actor Z:
This function makes the actor rotate on the Z axis (Yaw).
Blueprint:
Inherited from SoundSourceActor.cpp file:
void ASoundSourceActor::RotateActorZ(float Delta, float DegreesPerSecond) { // Creates a new Rotation multiplying the Delta time to DegreesPerSecond in the Y (Yaw) Rotator. const FRotator Rotation = FRotator(0,Delta * DegreesPerSecond,0); AddActorWorldRotation(Rotation); // Adds the new Rotator to this actor. }
.cpp file:
Event Tick
This functionality is implemented in Blueprints. The tick function triggers the RotateActorZ() on every frame.
BP_MultiPosSoundSourceActor
I created this Blueprint class based on the BP_SoundSourceActor Blueprint class.
Set Five Sound Positions:
This function sets five emitter positions on the Ak Component. Internally, this function creates an array of five float offsets to create an array of Transforms iterating with a For Loop.
Blueprint:
Inherited from SoundSourceActor.cpp file:
void ASoundSourceActor::SetFiveSoundPositions(float OffsetY1, float OffsetY2) { // An array of float Offsets. TArray OffsetsArray{ 0.0f, // Actors location + No Offset. OffsetY1, // Actors location + Offset 1. OffsetY2, // Actors location + Offset 2. OffsetY1 * -1.0f, // Actors location + negative Offset 1. OffsetY2 * -1.0f}; // Actors location + negative Offset 2. TArray<FTransform> PositionsArray; // An empty array of Transforms. for(const float Offset : OffsetsArray) // A for loop to iterate over the OffsetsArray values. { FTransform NewTransform = GetActorTransform(); // Gets this actor's transform. const FVector ActorLocation = GetActorLocation(); // Gets this actor's location. // Sets the NewTransform location adding each offset from the OffsetArray. NewTransform.SetLocation(FVector(ActorLocation.X, ActorLocation.Y + Offset, ActorLocation.Z)); PositionsArray.Add(NewTransform); // Adds the NewTransform to the PositionsArray. } // Uses the PositionsArray to set multiple positions for this AK Component. UAkGameplayStatics::SetMultiplePositions(AkComponent, PositionsArray, AkMultiPositionType::MultiSources); }
.cpp file:
BP_SplineSoundSourceActor
I created this Blueprint class based on the BP_SoundSourceActor Blueprint class.
Constructor:
For this class it is important to override the C++ class constructor, and enable the Spline Component auto-activation.
Blueprint:
Spline Follow Player Character:
This function moves the Ak Component along the spline based on the Player Character's position.
Blueprint:
Inherited from SoundSourceActor.cpp file:
void ASoundSourceActor::SplineFollowPlayerCharacter() { if(SplineComponent->IsActive()) // Continue if the Spline Component is active. { // Gets the closest Player Character's Location along the Spline Component. const FVector PlayerCharacterLocation = SplineComponent->FindLocationClosestToWorldLocation (PlayerCharacter->GetActorLocation(), ESplineCoordinateSpace::World); // Sets the Ak Component to this new location along the Spline Component. // The Sphere Component is attached to the Ak Component, so it will move too. AkComponent->SetWorldLocation(PlayerCharacterLocation); } else // If the Spline Component is not active, prints a Debug message. { GEngine->AddOnScreenDebugMessage(3, 10.0f, FColor::Red, "Spline Component is not active"); } }
.cpp file:
Event Tick
This functionality is implemented in Blueprints. The tick function triggers the SplineFollowPlayerCharacter() on every frame.