소개
이번 글은 Jater (Ruohao) Xu가 쓴 3부작 기술 블로그 시리즈의 마지막 편으로, 역붕괴: 베이커리 작전에 대한 작업을 공유합니다.
- Wwise를 사용하여 게임 내 시네마틱을 구현한 내용을 다룬 첫 번째 글은 여기에서 확인할 수 있습니다.
- 게임의 기울어진 2D 탑다운 시점으로 인한 독특한 감쇠(attenuation) 문제를 해결하기 위해 커스텀 3D 오디오 시스템이 필요했던 과정을 다룬 두 번째 글은 여기에서 확인하실 수 있습니다.”
Wwise Meter 플러그인을 활용한 애니메이션 립싱크
기술 블로그 시리즈 | 3편
게임에서는 오디오가 게임플레이 메커니즘을 주도하는 요소와 순간들이 곳곳에 존재합니다. Wwise Meter 플러그인을 이용하면 실시간으로 정확한 오디오 데이터를 수집하고 이를 게임 엔진에 전달하여 다양한 오디오 시스템을 구동할 수 있습니다.
다른 많은 일본 애니메이션 스타일의 게임들처럼 '역붕괴: 베이커리 작전'도 풍부한 스토리 대사를 포함하고 있습니다. 일부는 전투 중에 발생하지만, 대부분은 화면 좌우에 위치한 두 캐릭터가 교대로 대사를 주고받는 2D 내러티브 형식으로 구성됩니다.
위 이미지는 게임 내 2D 내러티브 시스템의 예시를 보여주고 있으며, 스크린샷이 찍힌 시점에는 멘도(Mendo) 캐릭터가 대사를 하고 있습니다. 대사를 시작하면 캐릭터 스프라이트에 립싱크 애니메이션이 재생됩니다. 이 기능은 Wwise로부터 수집한 오디오 볼륨 데이터를 기반으로 동작합니다.
게임은 오디오 볼륨 데이터를 활용해 대사와 립 애니메이션을 동기화함으로써 캐릭터 간 상호작용의 몰입감과 현실감을 높입니다. 이 방식은 내러티브 경험에 깊이를 더해 플레이어의 몰입도를 향상시킬 수 있습니다.
실시간으로 볼륨 데이터를 수집하기 위해서는 Wwise Meter 플러그인(Wwise Meter (audiokinetic.com))을 사용합니다. 이 플러그인은 사용이 간편하면서도, Wwise에서 오디오 데이터를 게임 엔진으로 전달하는 데 매우 효과적입니다. 아래 이미지는 주요 대사 버스(main speech bus)에 설정된 미터(meter) 설정을 보여줍니다.
Wwise Meter 플러그인 내부에서, 게임 엔진으로 데이터를 전달하는 역할을 하는 Speech_MeteringData라는 이름의 RTPC를 연결했습니다. 이 RTPC는 게임 내에서 발생한 대사의 출력 볼륨 정보를 수집합니다. 오디오 볼륨 레벨 범위를 -48에서 0 사이로 제한합니다. 대사 볼륨이 최고조에 달하면 0 값을 초과할 수도 있지만, 일반적인 믹스 설정에서는 이 상황을 피하는 것이 권장되며, 값을 0 이하로 유지시키는 것이 좋습니다.
이러한 구성을 통해 제어된 방식으로 오디오 볼륨 데이터를 정확하게 수집하여 게임 엔진에 전달할 수 있어 다양한 게임플레이 메커니즘을 구현하기가 쉬워집니다.
위 내용으로 Wwise에서의 설정 작업을 마무리합니다. 게임 엔진 쪽에서 데이터를 사용하려면, 볼륨 범위를 감지하고 해당 값을 애니메이션 시스템에서 사용할 수 있는 데이터로 변환하는 몇 줄의 코드만 추가하면 됩니다.” 게임마다 애니메이션 시스템이나 플러그인이 다르기 때문에 여기서 제시된 애니메이션 코드는 대략적인 예시로 작성되었습니다.
저희 게임의 경우 복잡한 애니메이션 시스템을 사용하지 않으며, 캐릭터의 입은 열림(Open)과 닫힘(Closed) 상태만 있기 때문에, 단순한 3항 조건 연산자를 사용해 말하고 있는 캐릭터의 입을 열지 여부를 판단하고 그에 따라 애니메이션을 적용할 수 있습니다. (GetGlobalRTPC() 구현에 대해서는 위 문단을 참고하세요.
bool bIsCharacterMouthOpen = (GetGlobalRTPC(“Speech_MeteringData”) > -48.0f && GetGlobalRTPC(“Speech_MeteringData”) <= 0) ? true : false;
다른 많은 게임들, 특히 3D 게임에서는 캐릭터의 뼈대 구성에 관절과 뼈가 포함되어 있을 수 있으며, 애니메이터가 입 주변 관절의 각도를 조절하여 입 열림 정도를 표현할 수 있습니다. 일반적으로 이 값은 부동 소수점(float)으로 표현됩니다. 예를 들어 여기에서는 speakingCharacter.SetMouthOpenness(float mouthJointAngle) 함수를 통해 이 값을 얻을 수 있다고 가정하며, 이때 입 열림 각도의 최소값은 0도, 최대값은 speakingCharacter.MaxMouthOpenness()도입니다.
이 예제에서는 매개변수 수정자의 출력 값을 추출하고, 이를 원하는 영역에서 필요에 따라 적용할 수 있도록 작은 래퍼 함수를 생성합니다.
public float GetGlobalRTPC(string rtpcName)
{
int rtpcType = 1;
float acquiredRtpcValue = float.MaxValue;
AkSoundEngine.GetRTPCValue(rtpcName, null, 0, out acquiredRtpcValue, ref rtpcType);
if(acquiredRtpcValue >= 0.25 && acquiredRtpcValue <= 16)
{
return acquiredRtpcValue;
}
else
{
return 1.0f;
}
}
위 함수는 RTPC를 전역으로 설정하는 것 외에도 잘못된 값이 감지되면 설정하려는 RTPC 값을 무시하고 기본값인 1.0f로 재설정합니다.
이 경우, 다음 함수를 사용하여 위 코드를 개선하고 해당 기능을 지원할 수 있습니다.
public float SetMouthOpenessByWwiseAudio()
{
float mouthOpennessToSet = 0.0f;
float retrievedMeteringRTPCvalue = GetGlobalRTPC(“Speech_MeteringData”);
if (retrievedMeteringRTPCvalue > -48.0f && retrievedMeteringRTPCvalue <= 0)
{
mouthOpennessToSet = speakingCharacter.MaxMouthOpenness() * Normalization(retrievedMeteringRTPCvalue, -48.0f, 0.0f));
}
speakingCharacter.SetMouthOpenness(mouthOpennessToSet);
}
실제로, 제공된 함수는 Wwise Meter 플러그인으로부터 받은 오디오 볼륨 데이터를 기반으로 입 열림 정도를 정확하게 설정해줍니다. 이를 통해 입 모양 애니메이션이 오디오 볼륨과 정확하게 일치하며 매끄럽게 동기화됩니다.
주의: 이 글에서 사용된 코드 예제들은 설명을 위해 일반화하여 재구성된 버전입니다. 기본 로직은 정상적으로 동작하는 것이 검증되었으며, 특정 프로젝트에 특화된 API 호출과 함수들은 저작권 문제로 인해 예제에서 생략되었습니다.
댓글