Espacialización #1 en Unreal Engine y Wwise (Blueprints y C++)
If you prefer to read an English version, click on this button =>
Unreal Engine Version: 5.0.3
Wwise Version: 2021.1.10.7883
Este post muestra cuatro técnicas de espacialización en Unreal Engine 5 y Wwise. En este post te enseño cómo implementar un Sonido 3D, un Sonido Cone Attenuated, un Sonido Multi-Position, y un sonido que sigue al oyente usando un Spline Component. Todos estos sonidos son disparados usando la función OnComponentBeginOverlap() en el Sphere Component del actor. Adicionalmente, este proyecto ofrece un buen ejemplo de cómo se heredan clases entre clases C++ y Blueprints en Unreal Engine 5.
Implementé el mismo sistema dos veces, uno usando scripting visual en Blueprints, y otra usando código C++ nativo. Aquí una lista de herramientas y palabras clave para este proyecto:
Programé todo el sistema dentro de la clase padre C++ SoundSourceActor, y las clases Blueprint BP_SoundSourceActor, BP_RotationSoundSourceActor, Bp_MultiPosSoundSourceActor, and BP_SplineSoundSourceActor.
Importante:
Ten en consideración que esta guía no pretende enseñar diseño de sonido creativo. En cambio, se enfoca exclusivamente en aspectos de implementación y programación de game audio.
Descarga el Proyecto Aquí:
IMPORTANTE!
〰️
IMPORTANTE! 〰️
Prerequisitos:
Crea un proyecto C++ third-person vacio y llámalo “MyUE5Project”.
Descarga e integra el plugin Wwise en tu proyecto por medio del Wwise Launcher.
Activa la opción de Event-Based Packaging.
Para que este tutorial tenga sentido, es importante que sepas cómo integrar el plugin Wwise en Unreal Engine. Adicionalmente, considera leer y consultar mi post anterior sobre UPROPERTY & UFUNCTION:
IMPORTANTE!
〰️
IMPORTANTE! 〰️
Setup Wwise:
Dentro del proyecto de Wwise, creé cuatro eventos. Cada evento reproduce un blend container que contiene un sonido espacializado, y una narración de tipo Voice-Over no espacializada.
Cada blend container implementa diferentes configuraciones de posicionamiento, incluidos diferentes ShareSets de atenuación.
Viewport del Proyecto:
Cada sonido es emitido por un Ak Component dentro de un Actor Blueprint inside a Blueprint Actor heredado de la clase C++ SoundSourceActor. Cada actor tiene un Sphere Component que dispara el sonido y también muestra el rango de la atenuación por distancia.
Setup C++:
Creé una clase C++ padre llamada SoundSourceActor con todos los componentes, propiedades y funciones requeridas por todas las clases hijas. Estas propiedades y funciones son expuestas e implementadas en Blueprints.
Importante:
Yo uso comentarios de tipo Doxygen para explicar el código. Estos comentarios no van a comprometer la funcionalidad del código o su compilación. Siéntete libre de copiar el código tal como está escrito aquí.
Build.cs:
Dentro de MyUE5Project.Build.cs en MyUE5Project > Source, Añadí "AkAudio" cómo una dependencia del build.
// Añade "AkAudio" para usar y compilar el API de Wwise en C++ PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "AkAudio" });
Declaración de Funciones y Propiedades:
En el Content Drawer, creé una clase C++ llamada SoundSourceActor.
Encuentra esta clase en MyUE5Project > Source > SoundSourceActor.h
Estas son las directivas incluidas:
.h file:
#include "AkComponent.h" // Incluye esta directiva para acceder a Ak Component. #include "Components/SphereComponent.h" // Incluye esta directiva para acceder a Sphere Component. #include "Components/SplineComponent.h" // Incluye esta directiva para acceder a Spline Component.
Declaré un Static Mesh Component, un Ak Component, un Sphere Component, un Spline Component, un Player Character, un Booleano, y un FString .
.h file:
/** El static mesh de este actor. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") UStaticMeshComponent* StaticMesh; /** El Ak Component de este actor - Contiene un evento de audio y permite su espacialización. */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Audio") UAkComponent* AkComponent; /** Un sphere component para generar eventos de tipo overlap. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") USphereComponent* SphereComponent; /** Un spline permite a este actor seguir la ubicación del player character. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") USplineComponent* SplineComponent; /** Una referencia del player character. */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Audio") ACharacter* PlayerCharacter; /** Una propiedad booleana que permite activar los mensages de debug. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") bool bDebug = true; /** El mensage debug que se muestra en pantalla. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Audio") FString DebugMessage;
Declaré cinco funciones: PlayAudioEvent(), RotateActorZ(), SetFiveSoundPositions(), SplineFollowPlayerCharacter(), y ClearOnScreenDebugMessages().
.h file:
/** * @brief Reproduce o detiene el evento relacionado con este AkComponent. Imprime un mensaje de Debug. * @param bPlay true = Play | false = Stop. * @param Debug true = Debug | false = No Debug. * @param _DebugMessage El mensaje de Debug */ UFUNCTION(BlueprintCallable, Category="Audio") void PlayAudioEvent(bool bPlay, bool Debug, const FString& _DebugMessage); /** * @brief Rota al actor the actoren el Z rotator (Yaw). * @param Delta La diferencia de tiempo entre el frame anterior y el actual. * @param DegreesPerSecond Cuantos grados rota este actor cada segundo. */ UFUNCTION(BlueprintCallable, Category="Audio") void RotateActorZ(float Delta, float DegreesPerSecond); /** * @brief Crea cinco transforms para generar un sonido multi-posicionado. * @param OffsetY1 Una compensación del vector cero de este actor. * @param OffsetY2 Una compensación del vector cero de este actor. */ UFUNCTION(BlueprintCallable, Category="Audio") void SetFiveSoundPositions(float OffsetY1, float OffsetY2); /** Sigue la ubicación de Player Character location a lo largo del Spline Component. * Debe ser llamado desde una función tipo Tick */ UFUNCTION(BlueprintCallable, Category="Audio") void SplineFollowPlayerCharacter(); /** Función custom para borrar todos los mensajes en pantalla. * Expuesto a blueprints. */ UFUNCTION(BlueprintCallable, Category="Audio") void ClearOnScreenDebugMessages();
Constructor y Directivas en el archivo .cpp:
En el archivo SoundSourceActor.cpp, incluye estas directivas.
.cpp file:
#include "GameFramework/Character.h" // Incluye esta directiva para acceder a las funciones de ACharacter. #include "AkGameplayStatics.h" // Incluye esta directiva para acceder a las funciones Wwise. #include "Kismet/GameplayStatics.h" // Incluye esta directiva para acceder a la función GetPlayerCharacter().
Componentes en el constructor de la clase:
Creé todos los componentes en el constructor, y definí las conexiones entre ellos:
Static Mesh es el Root Component
Ak Compoment conectado con Static Mesh(Root Component)
Sphere Component conectado con Ak Component
Spline Compoment conectado con Static Mesh(Root Component)
.cpp file:
// Crea un Static Mesh Component. StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent")); SetRootComponent(StaticMesh); // Define al Static Mesh como el Root Component de este actor. // Crea un Ak Component. AkComponent = CreateDefaultSubobject<UAkComponent>(TEXT("Ak Component")); AkComponent->SetupAttachment(RootComponent); // Conecta este Ak Component al Root Component. // Crea un Sphere Component. SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); SphereComponent->SetupAttachment(AkComponent); // Conecta este Spline Component al Ak Component. SphereComponent->SetHiddenInGame(false); // Define la visibilidad de este componente en el gameplay. // Crea un Spline Component. SplineComponent = CreateDefaultSubobject<USplineComponent>(TEXT("SplineComponent")); SplineComponent->SetupAttachment(RootComponent); // Conecta este Spline Component al Root Component. SplineComponent->SetAutoActivate(false); // Inicializa a este componente como desactivado. Es activado en la clase "BP_SplineSoundSourceActor".
BP_SoundSourceActor (3D Sound)
Creé esta clase Blueprint basada en la clase C++ SoundSourceActor.
Play Audio Event:
Esta función reproduce o detiene al Ak Event.
Blueprint:
Heredado del archivo SoundSourceActor.cpp:
void ASoundSourceActor::PlayAudioEvent(bool bPlay, bool Debug, const FString& _DebugMessage) { if(AkComponent->AkAudioEvent) // Continua si hay un Ak Audio Event asignado. { if(bPlay) // Continua si bPlay = true. { // Reproduce el Ak Audio Event asociado. AkComponent->PostAssociatedAkEvent(0, FOnAkPostEventCallback(), TArray<FAkExternalSourceInfo>()); if (Debug) // Imprime un mensaje de debug si Debug = true. { GEngine->AddOnScreenDebugMessage(1, 60.0f, FColor::Green, _DebugMessage); GEngine->AddOnScreenDebugMessage(2, 60.0f, FColor::Red, "C++ Implementation"); } } else // Detiene al Ak Audio Event y borra todos los mensajes de Debug si bPlay = false. { AkComponent->Stop(); ClearOnScreenDebugMessages(); } } }
.cpp file:
On Component Begin/End Overlap
Esta funcionalidad está implementada en Blueprints y es heredada a todos los hijos de la clase BP_SoundSourceActor. Cuando el Character se superpone con el Sphere Component, el Ak Event se reproduce. El evento se detiene cuando el Character sale de la esfera.
Blueprint:
BP_RotationSoundSourceActor (Cone Att.)
Creé esta clase Blueprint basada en la clase C++ SoundSourceActor.
Rotate Actor Z:
Esta función hace al actor rotar en el eje Z (Yaw).
Blueprint:
Heredado del archivo SoundSourceActor.cpp:
void ASoundSourceActor::RotateActorZ(float Delta, float DegreesPerSecond) { // Crea una nueva Rotación al multiplicar el tiempo Delta por DegreesPerSecond en el Rotator Y (Yaw). const FRotator Rotation = FRotator(0,Delta * DegreesPerSecond,0); AddActorWorldRotation(Rotation); // Adds the new Rotator to this actor. }
.cpp file:
Event Tick
Esta funcionalidad está implementada en Blueprints. La función Tick dispara a RotateActorZ() en cada frame.
BP_MultiPosSoundSourceActor
Creé esta clase Blueprint basada en la clase C++ SoundSourceActor.
Set Five Sound Positions:
Esta función define cuatro posiciones de emisión en el Ak Component. Internamente, esta función crea un array con cinco compensaciones de tipo float para crear un array de Transforms al iterar con un For Loop.
Blueprint:
Heredado del archivo SoundSourceActor.cpp:
void ASoundSourceActor::SetFiveSoundPositions(float OffsetY1, float OffsetY2) { // Un arrary de compensaciones float. TArray OffsetsArray{ 0.0f, // La ubicación del actor + No Compensación. OffsetY1, // La ubicación del actor + Compensación 1. OffsetY2, // La ubicación del actor + Compensación 2. OffsetY1 * -1.0f, // La ubicación del actor + Compensación 1 negativa. OffsetY2 * -1.0f}; // La ubicación del actor + Compensación 2 negativa. TArray<FTransform> PositionsArray; // Un array de Transforms vacio. for(const float Offset : OffsetsArray) // Un for loop para iterar sobre los valores de OffsetsArray. { FTransform NewTransform = GetActorTransform(); // Obtiene el transform de este actor. const FVector ActorLocation = GetActorLocation(); // Obtiene la ubicación de este actor. // Define la ubicación de NewTransform al añadir cada compensación de OffsetArray. NewTransform.SetLocation(FVector(ActorLocation.X, ActorLocation.Y + Offset, ActorLocation.Z)); PositionsArray.Add(NewTransform); // Añade el NewTransform a PositionsArray. } // Usa el PositionsArray para definir múltiples posiciones para este AK Component. UAkGameplayStatics::SetMultiplePositions(AkComponent, PositionsArray, AkMultiPositionType::MultiSources); }
.cpp file:
BP_SplineSoundSourceActor
Creé esta clase Blueprint basada en la clase C++ SoundSourceActor.
Constructor:
Para esta clase es importante sobreescribir al constructor de la clase C++ y habilitar la auto-activación del Spline Component.
Blueprint:
Spline Follow Player Character:
Esta función mueve al Ak Component a lo largo del spline basado en la posición del Player Character.
Blueprint:
Heredado del archivo SoundSourceActor.cpp:
void ASoundSourceActor::SplineFollowPlayerCharacter() { if(SplineComponent->IsActive()) // Continua si el Spline Component está activo. { // Obtiene la ubicación más cercana del Player Character a lo largo del Spline Component. const FVector PlayerCharacterLocation = SplineComponent->FindLocationClosestToWorldLocation (PlayerCharacter->GetActorLocation(), ESplineCoordinateSpace::World); // Define la ubicación del Ak Component a esta nueva ubicación a lo largo del the Spline Component. // El Sphere Component está conectado al Ak Component, entonces se va a mover también. AkComponent->SetWorldLocation(PlayerCharacterLocation); } else // Si el Spline Component no está activo, imprime un mensage de debug. { GEngine->AddOnScreenDebugMessage(3, 10.0f, FColor::Red, "Spline Component is not active"); } }
.cpp file:
Event Tick
Esta funcionalidad está implementada en Blueprints. La función tick dispara a SplineFollowPlayerCharacter() en cada frame.