Post MIDI On Event en Wwise y Unreal Engine
If you prefer to read an English version, click on this button =>
Unreal Engine Version: 5.3
Wwise Version: 2023.1.1.8417
En este blog post, muestro cómo usar la funcionalidad MIDI de Wwise MIDI para disparar sonidos de armas con precisión al nivel de muestras de audio (sample-accurate). También muestro cómo usar la función AK::SoundEngine::PostMIDIOnEvent en C++ para crear secuencias MIDI sample-accurate. Adicionalmente, muestro cómo configurar una función callback desde el motor de audio para llamar acciones de gameplay.
A pesar que esta funcionalidad está incluida en el SDK de Wwise - Unreal, no está del todo integrada en el framework del motor de juego, entonces es solo accesible por medio de C++. La posibilidad de integrar Blueprints es posible después de configurar el sistema en C++.
Aquí la lista de herramientas y términos esenciales usados en este proyecto:
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.
Usé los assets del personaje Paragon: Lt. Belica de el Unreal Engine Marketplace.
Este sistema no está oficialmente mantenido en Unreal. La documentación es pobre, y requiere de cierta manipulación de múltiples hilos. Ten cuidado de usarlo en código de producción!
Descarga el Proyecto Aquí:
IMPORTANTE!
〰️
IMPORTANTE! 〰️
Considera leer y consultar mi post anterior sobre UPROPERTY & UFUNCTION:
IMPORTANTE!
〰️
IMPORTANTE! 〰️
Configuración de Wwise
Este sistema usa tres eventos: un disparo, un impacto, y la cola del disparo. El disparo llama a un blend container con varios random containers. Cada sonido tiene su propia atenuación. Estos sonidos son enrutados al bus “Weapons” y enviados a un bus auxiliar para dar un poco más de color.
El evento “Gunshot_MIDI” NO ES UN LOOP!
Para compilar y usar correctamente el plugin Wwise, añadí estas dependencias en el archivo Build.cs:
Ranged Character Class - Lt. Belica
Cree una clase C++ derivada de ACharacter llamada HRangedCharacter. Después, importé el personaje blueprint de Paragon: Lt. Belica. Me aseguré que este Blueprint derive de HRangedCharacter
En el constructor de la clase, configuré una posición y rotación relativa, y creé un AkWwise Component vinculado al socket de la arma de fuego.
HRangedCharacter.h:
HRangedCharacter.cpp:
Post MIDI On Event
Parámetros
Estos son los parámetros y assets necesarios para este sistema. Estos son editables o visibles en Blueprint al usar el macro UPROPERTY.
HRangedCharacter.h:
Función #1: Fire Burst On MIDI
Esta función obtiene el motor de audio de Wwise y prepara un array de post MIDI que van a ser sincronizados por AK::SoundEngine::PostMIDIOnEvent(). Usé dos distintos velocidades MIDI para diferenciar el último disparo del resto. Esto me permite llamar al sonido de la cola cuando el último disparo de la secuencia se reproduzca.
AK_MIDIEvent y AK_EndOfEvent le dicen al Callback que responda a estos eventos en específico.
Esta función no requiere de todos sus parámetros, pero en este caso incluí un Callback estático (OnPostMidiCallback) y una función delegada (OnFireWeapon) como un “Cookie.”
Puedes proveer cualquier puntero o dato como un Cookie. El Callback va a llevar consigo este puntero el cual puede ser usado después.
HRangedCharacter.cpp:
Función #2: On Post MIDI Callback
Esta función debe ser estática y requiere de una firma específica requerida por PostMIDIOnEvent.
Desafortunadamente, sus parámetros no están definidos cómo UOBJECTS, entonces no hay forma de exponer esta función a Blueprint o vincularlo a un delegado de Unreal en este punto.
Es importante entender que por lo pronto este proceso está corriendo en el hilo de audio. Algunos sistemas de juego (animaciones, partículas) pueden generar un crash en el juego si son invocadas desde fuera del hilo de gameplay. Para solucionar este problema, dentro de esta función estoy usando un AsyncTask() para mover desde el hilo de audio al de gameplay.
Finalmente, usé el static_cast<> nativo de C++ para obtener el delegado OnFireWeapon que incluí cómo Cookie. Finalmente, lo invoqué con BroadCast()
HRangedCharacter.h:
HRangedCharacter.cpp:
Función #3: Handle On Fire Weapon and On Fire Delegate
Creé un delegado Unreal llamado OnFireWeapon que es llamado en OnPostMidiCallback. Este delegado está ahora corriendo en el hilo de gameplay, entonces es posible vincular a este cualquier funcionalidad de juego.
HRangedCharacter.h:
Usé la función PostInitializeComponents() incluida en todo tipo de AActor para vincular este delegado a la siguiente función de este proceso: HandleOnFireWeapon()
HRangedCharacter.cpp:
HandleOnFireWeapon dispara un line trace, genera los efectos de partículas de muzzle flash y de impactos de bala sincronizado a cada evento MIDI. Si bLastShot es verdadero, también llama a la función FireShotTail()
HRangedCharacter.h:
Función #4: Fire Shot Tail
Esta función es simple. Reproduce el evento de cola cuando la velocidad MIDI es menor a 127. Este es el sistema que definí para identificar el último disparo.
HRangedCharacter.cpp:
End Play
El motor de audio persiste entre el juego y el editor, entonces es importante detener todos los eventos MIDI en caso de que el actor sea destruido o el juego termine mientras la secuencia MIDI todavía se esté reproduciendo.
HRangedCharacter.cpp:
BP_RangedCharacter y el principio del proceso
En la clase Blueprint del personaje, añadí un Timer By Function Name para disparar todos los sistemas en un ritmo consistente. También llamé a la función Play Animation Montage con “Primary_Fire_Med_Montage” incluida en el asset pack de Lt. Belica. Cooldown Timer y Start Delay son parámetros creados en Blueprint y expuestos al editor para mayor conveniencia.
Múltiples intancias de BP_RangedCharacter?
Creé un segundo nivel con tres instancias deI personaje, cada uno con diferentes parámetros. Fíjate que cada instancia responde al callback estático sin ningún problema.