버전

menu_open
Wwise SDK 2019.1.11
Wwise 뱅크 관리 예제

게임에서 뱅크를 어떻게 관리할 지 결정하기에 앞서 다음 문서를 자세히 읽어보시기 바랍니다.

다양한 뱅크 관리 방법 이해하기

소개

유연성을 최대한 확보하고 대부분 게임 유형의 요구 사항을 충족시키기 위해 Wwise에서는 게임 내 뱅크 관리 방법과 관련해 새로운 접근 방식을 제공하고 있습니다. 이 새로운 방식은 기존 방식을 그대로 사용하면서도 좀 더 효과적인 제어권과 유연성을 제시해 자신의 게임의 요구 사항을 좀 더 효율적으로 관리할 수 있도록 돕습니다.

Wwise의 새로운 접근 방식은 기존 방식에 비해 다음 세 가지 부분이 개선됐습니다.

  • SoundBank를 구성할 때, 이벤트뿐만 아니라 구조화된 콘텐츠(사운드, Container, Actor-Mixer 등)나 Work Unit, 폴더로도 채워넣을 수 있습니다.
  • 뱅크에 어떤 종류의 정보를 포함시킬 지 정할 수 있습니다. 즉, 미디어, 구조화된 데이터, 이벤트 콘텐츠 중 한 종류로만 뱅크를 채워넣을 수도 있고, 세 개를 결합해 구성할 수도 있습니다. 예를 들어 특정 이벤트와 관련된 미디어로만 구성된 뱅크를 생성할 수 있습니다.
  • SoundBank에서 특정 요소를 포함시키거나 제외할 수 있습니다.

이 새로운 접근 방식의 주요 장점은, 미디어 콘텐츠를 여러 메모리 뱅크에 나눠 넣을 수 있다는 점입니다. 예를 들어, 전체 게임 음악이 하나의 이벤트로 시작된다고 가정합시다. 기존 방식을 이용할 경우 뱅크에 이벤트를 추가하면, 게임 끝에서만 재생되는 음악까지 포함해 메모리 내 관련 사운드와 미리 가져온 미디어까지 전부 자동으로 추가하게 됩니다. 게임 전체의 모든 미디어를 메모리에 저장하는 이런 방식은 매우 비효율적입니다. 그러나 Wwise의 새로운 접근방법을 이용하면 음악 미디어를 여러 뱅크로 나눠 넣어 특정 사운드가 재생돼야 할 때에만 로드하게 돼, 메모리를 효율적으로 관리할 수 있습니다.

여러 뱅크에 미디어를 나눠 넣으면 로드해야 할 미디어의 우선순위를 정할 수도 있습니다. 예를 들어, 메모리가 제한적인 경우 가장 중요한 미디어를 먼저 로드해야 할 것입니다. 비교적 덜 중요한 미디어는 별도의 뱅크에 저장해 메모리 공간이 충분할 때에만 로드할 수 있습니다. 예전에는 중요한 미디어와 중요하지 않은 미디어 파일 모두 동일한 뱅크에 포함됐었습니다. 한 뱅크가 메모리에 비해 너무 클 경우, 중요한 사운드는 커녕 심지어 아무 사운드도 재생되지 않는 경우도 있었습니다.

다양한 뱅크 관리 방법

다음에 나올 부분은, 게임에 뱅크를 생성하고 통합하는 다양한 방법에 대해 설명합니다. 하나의 게임에서도 한 가지 이상의 방법을 조합하여 사용할 수 있습니다. 모든 게임은 서로 다르기 때문에 게임의 요구 사항에 따라 알맞은 방법을 선택해야 할 것입니다.

뱅크를 생성할 때 선택하는 내용에 따라 게임 내 오디오 에셋을 관리하는 작업량이 크게 좌우되며, 게임의 성능에 직접적인 영향을 끼칩니다. 사운드 디자이너와 오디오 프로그래머가 함께 이 문서를 꼼꼼히 살펴보고 앞으로 발생할 가능성을 예측해보는 것이 중요합니다. 협업을 통해 게임에서 중요시 하는 내용에 따라 적절한 전략을 세울 수 있을 것입니다. 어떤 솔루션이든 사용할 수 있지만, 메모리 사용량, I/O 접근, 게임 통합(integration)의 용이성을 고려해서 가장 적합한 전략을 선택해야 합니다. 각 방법에는 장점과 단점이 있기 때문에 대부분의 경우 메모리 사용과 통합의 용이성 사이의 균형을 찾는 것이 관건입니다.

방법 1: 하나의 '일체형' 뱅크

이 방법을 사용하면 모든 이벤트 콘텐츠와 사운드 구조 데이터, 미디어 파일이 하나의 뱅크에 함께 저장되며 전부 동시에 메모리에 로드됩니다.

이 방법은 다음과 같은 상황에 적용할 수 있습니다.

  • 게임에 사용하는 오디오 에셋의 양이 제한적인 경우
  • 게임에 사용할 수 있는 메모리가 충분한 경우

물론 대부분의 게임은 보통 낭비할 만한 메모리가 없지만 이 방법은 사용하기와 유지하기가 아주 쉽다는 장점이 있습니다. 이 방법을 사용하는 가장 주된 이유는 바로 Wwise 프로젝트 전체를 최소 시간 안에 게임에 통합해야 할 경우입니다.

사용 방법:

Wwise에서

  1. SoundBank 하나를 생성하고 이름을 지정합니다.
  2. SoundBank Editor로 뱅크를 불러온 후 다음 아이템을 'Add' 탭으로 드래그합니다.
    • \Actor-Mixer Hierarchy\Default Work Unit
    • \Events\Default Work Unit
    • \Interactive Music Hierarchy\Default Work Unit (상호작용 음악을 사용할 경우에만)
    • \Dynamic Dialogue\Default Work Unit (동적 대사를 사용할 경우에만)
      참고: Events, Structures, Media 열 아래 체크 상자는 기본적으로 모두 선택되어 있습니다. 뱅크에 모든 것을 넣을 것이기 때문에 이렇게 이미 모두 선택된 것이 용이합니다.

      작은 정보: 뱅크에 프로젝트 요소를 개별적으로 추가하지 않고 전체 Work Unit을 통째로 추가하면 Work Unit 콘텐츠의 변경 내용을 반영시키기 위해 뱅크를 재편집할 필요가 없다는 장점이 있습니다. Wwise는 SoundBank 안의 요소와 프로젝트의 요소 간에 활성화된 링크를 유지하기 때문입니다. 프로젝트 내 Work Unit에 변경사항이 생기면 자동으로 SoundBank가 업데이트됩니다. 이러한 방법을 사용하면 간단히 Generate 버튼을 누르는 것만으로 새로운 SoundBank 세트를 생성할 수 있습니다.
      참고: 새로운 프로젝트에서는 기본 Work Unit만 사용 가능합니다. Work Unit이 더 만들어진 경우 이 Work Unit 또한 필요에 따라 뱅크에 추가되어야 합니다.
  3. 모든 Work Unit을 SoundBank에 추가한 후 뱅크를 생성한 다음, 생성된 이 SoundBank 폴더를 게임 애플리케이션에 복사하세요.

게임에서

이 게임에는 SoundBank가 한 개만 존재하기 때문에, 게임을 초기화할 때 그냥 이 뱅크를 불러오면 됩니다. 물론 사운드 엔진이 반드시 먼저 올바르게 초기화되어야 합니다.

...
// 여기에서 사운드 엔진을 초기화하세요.
...
// Init 뱅크와 '일체형' 뱅크를 불러오세요.
AkBankID bankID; // 이 예제에서는 사용되지 않습니다.
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"MyAllInOneBank.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...

이 방법에 대한 추가 정보

장점:

  • 사운드 디자이너의 관점에서 뱅크의 콘텐츠를 관리하는 가장 쉬운 방법입니다.
  • 게임이 항상 동일한 뱅크를 불러오기 때문에 해당 뱅크의 콘텐츠를 변경하더라도 게임을 다시 컴파일할 필요가 없습니다.
  • 게임 안에서 뱅크를 로드하거나 언로드하는 복잡한 작업을 하지 않아도 됩니다.
  • 어떤 사운드가 사용 가능한 지 게임에서 추적해야 할 필요가 없습니다.

단점:

  • 전체 게임 동안 모든 이벤트, 구조, 메모리 내장 미디어가 항상 로드돼있기 때문에 메모리 사용이 비효율적입니다.
작은 정보: 이 방법은 게임 오디오를 빠르고 쉽게 통합하는 좋은 방법이지만, 프로젝트 개발이 끝나갈 때 즈음 게임 메모리 사용을 줄일 수 있는 방법으로 전환하는 일이 없도록 주의해야 합니다.

방법 2: 여러 개의 완전한 뱅크

이 방법은 다음과 같은 상황에 적용할 수 있습니다.

  • 게임이나 오디오 콘텐츠가 여러 섹션으로 나뉘어질 경우

이 방법은 모든 소리가 게임에서 플레이어의 현재 위치에 의해서만 구동되는 싱글 플레이어 게임에 적합합니다. 콘텐츠를 여러 개의 뱅크로 나누어 넣으면 첫 번째 방법보다 메모리를 더 효율적으로 관리할 수 있으며, 그래도 비교적 쉽게 오디오를 게임에 통합할 수 있다는 장점이 있습니다.

사용 방법:

가장 먼저 뱅크를 어떻게 분할할 지 결정해야 합니다. 예를 들어 다음과 같은 방식으로 뱅크를 분할할 수 있습니다.

  • 게임에서 언제든지 일어날 수 있는 모든 이벤트를 담은 하나의 일반 뱅크 이 뱅크는 메모리에 항상 로드돼있어야 합니다.
  • 레벨 또는 환경 당 하나의 뱅크 이 뱅크는 주요 캐릭터의 실제 위치에 따라 의존적인 사운드로 구성되어야 합니다.
  • 특정 게임의 요구 사항에 따라 필요한 추가적인 뱅크

Wwise에서

  1. 게임에 필요한 뱅크를 모두 만든 후, 'CommonEvents', 'Level_1', 'Level_2, 'Level_3'과 같이 각각 알맞은 이름을 지정합니다.
  2. Wwise에서 이벤트를 폴더로 묶습니다. 뱅크당 하나의 폴더를 만들어서 각 폴더를 해당 뱅크로 드래그합니다. SoundBank에 폴더를 추가하면 프로젝트에 새로운 이벤트가 추가될 때마다 SoundBank 콘텐츠를 편집하지 않아도 됩니다. 이 폴더의 콘텐츠가 변경되면 SoundBank가 자동으로 업데이트됩니다.
  3. 모든 이벤트를 각 뱅크 안에 넣습니다. 이 단계는 원래의 폴더 외부에 있는 이벤트를 추가할 경우에만 필요합니다. 한 이벤트가 여러 개의 뱅크에 들어가야 할 경우 필요한 모든 뱅크로 추가하면 됩니다.
  4. 뱅크를 생성한 다음 생성된 뱅크 폴더를 게임 애플리케이션에 복사합니다.

게임에서

게임에서 적절한 시기에 알맞은 뱅크를 불러옵니다. 예를 들어 게임 초반에 일반 뱅크를 불러오고 플레이어의 실제 위치에 따라서 다른 적절한 뱅크를 불러올 수 있습니다. 일부 게임에서는 레벨 사이 전환 효과를 위해 동시에 여러 개의 '레벨'을 불러올 메모리가 충분히 있어야 한다는 점을 주의하세요.

...
// 여기에서 사운드 엔진을 초기화하세요.
...
// Init 뱅크와 Common 뱅크를 불러옵니다.
AkBankID bankID; // 이 예제에서는 사용되지 않습니다.
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"CommonEvents.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...
// 필요에 따라 코드 내 원하는 위치에서 다음 내용을 입력합니다.
eResult = AK::SoundEngine::LoadBank( L"Level_1.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::LoadBank( L"Level_2.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::LoadBank( L"Level_3.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::UnloadBank( L"Level_1.bnk", NULL );
...
eResult = AK::SoundEngine::UnloadBank( L"Level_2.bnk", NULL );
...
eResult = AK::SoundEngine::UnloadBank( L"Level_3.bnk", NULL );

이 방법에 대한 추가 정보

장점:

  • '일체형' 뱅크 방식보다 메모리 사용량이 적을 수 있습니다.
  • 게임에 통합하기가 매우 쉽습니다.

단점:

  • 주요 캐릭터의 위치같이 단순한 사실보다 더 많은 요인에 대해 오디오 요구 사항을 충족시켜야 하는 온라인 게임이나 이벤트 중심의 게임에 적합하지 않습니다.
  • 뱅크에 중복된 데이터가 있을 수 있기 때문에 메모리에 오디오 음원이 중복돼 로드될 수 있습니다.
  • 여러 뱅크가 비슷한 콘텐츠를 가질 수 있기 때문에 뱅크의 전체 용량이 증가할 수 있습니다.

방법 3: 세부 미디어 관리

이 방법은 다음과 같은 상황에 적용할 수 있습니다.

  • 게임에 메모리 내장 미디어 에셋이 아주 많은 경우
  • 사운드 디자이너가 미디어의 요구 사항을 미리 예측하기 어려운 경우
  • 프로젝트가 특정 이벤트에서 Switch와 State를 사용해 재생할 소리를 결정할 경우
  • 사운드를 정의된 섹션으로 쉽게 나눌 수 없을 경우

사용 방법:

게임은 아주 복잡해질 수 있으므로 소리의 발생은 게임 텍스처, 시간대, 게임 오브젝트의 움직임, (온라인 멀티 플레이어 게임의 경우) 다른 플레이어의 동작과 같은 다양한 요인에 좌우됩니다. 예를 들어 이벤트나 오브젝트 중심의 환경에서는 다른 게임 오브젝트의 근접성에 따라 사운드를 메모리로 불러올 수 있습니다. 모든 게임은, 오브젝트가 주어진 범위 안에 있거나 단순히 존재할 경우 불러와야 하는 뱅크 목록을 가질 수 있습니다.

또한 Switch와 State가 재생할 사운드를 결정할 수 있습니다. 이벤트가 뱅크에 추가된 이런 사운드를 재생할 경우, 재생 가능한 모든 미디어가 자동으로 뱅크에 함께 추가됩니다. 예를 들어 'Play_Footstep'이라는 이름의 이벤트가 있다고 가정합니다. 이 이벤트는 현재 지면의 텍스처에 따라 알맞은 소리를 재생하며, 이 소리는 Switch가 변경되면서 지정됩니다. 이 방법도 잘 작동하지만, 게임플레이가 런던의 한 건물 안에서 일어나고 있을 경우 'footstep_sand.wav'(모래 발자국 소리)나 'footstep_winter.wav'(겨울 발자국 소리) 사운드를 메모리에 유지하는 것은 낭비일 것입니다.

이러한 메모리 낭비를 피하려면 이벤트 혹은 사운드 구조를 뱅크에 불러온 다음 이 뱅크에 들어갈 사운드를 지정할 수 있습니다. 예를 들어 여러 가지 지표면에 따른 발자국 소리를 사용할 경우, 다음과 같은 뱅크를 생성할 수 있습니다.

  • 'EventBank'는 'Play_Footstep' 이벤트와 구조를 담습니다.
  • 'Winter_Footstep_bank'는 게임의 겨울 부분에서만 일어나는 발자국 소리의 미디어 파일을 담습니다.
  • 'Desert_Footstep_bank'는 게임의 사막 부분에서만 일어나는 발자국 소리의 미디어 파일을 담습니다.
  • 'Common_Footstep_bank'는 나무나 콘크리트 등 언제든지 일어날 수 있는 발자국 소리의 미디어 파일을 담습니다.

Wwise에서

앞의 예제를 재생성하는 방법:

게임에 세 가지 다른 텍스처가 있다고 가정합시다 (눈, 모래, 콘크리트). Wwise에서 'ground_texture' Switch에 따라 세 개의 Random Container 중 하나를 재생하는 Switch Container가 있습니다. 세 가지 Random Container는 각각 주어진 텍스처에 따라 네 개의 다른 발자국 소리를 가지고 있습니다.

  1. 'EventBank'라는 이름의 뱅크를 만든 후 SoundBank Editor에 불러옵니다.
  2. 'Play_Footstep' 이벤트를 SoundBank Editor의 Add 탭으로 드래그합니다.
  3. 이 이벤트의 'Media' 체크 상자를 선택 해제합니다. 단, Events와 Structures 체크 상자는 선택한 상태로 유지합니다.
  4. 각 텍스처당 하나씩, 총 세 개의 뱅크를 만듭니다.
  5. 각 뱅크에서 이 텍스처에 연결된 Random Container를 드래그합니다.
    참고: Random Container 전체를 드래그해서 놓는 대신 각 사운드를 개별적으로 드래그해도 됩니다. 하지만 컨테이너를 사용하면 컨테이너 안의 모든 사운드가 자동으로 뱅크에 추가되기 때문에, 컨테이너의 내용이 변경될 경우 변경 사항을 수동으로 적용하지 않아도 된다는 장점이 있습니다.
  6. 세 개의 각 텍스처 뱅크에서 'Events'와 'Structures' 체크 상자를 선택 해제하고 Media 유형만 체크한 상태로 유지합니다.
  7. 뱅크를 생성한 다음 생성된 뱅크 폴더를 게임 애플리케이션에 복사합니다.

이제 네 개의 뱅크가 생겼습니다. 한 뱅크는 재생할 오디오에 관한 이벤트와 구조 데이터를 담고 있으며 나머지 세 뱅크는 특정 지표면에 연결된 미디어만 담고 있습니다.

게임에서

// Init과 EventBank를 불러옵니다
AkBankID bankID; // 이 예제에서는 사용되지 않습니다.
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"EventBank.bnk", AK_DEFAULT_POOL_ID, bankID );
}
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"Common_Footstep_bank.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...
// 적절한 위치에 따라 다음 내용을 코드 내 원하는 곳에 입력합니다.
eResult = AK::SoundEngine::LoadBank( L"Winter_Footstep_bank.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::LoadBank( L"Desert_Footstep_bank.bnk", AK_DEFAULT_POOL_ID, bankID );
...
eResult = AK::SoundEngine::UnloadBank( L"Winter_Footstep_bank.bnk", NULL );
...
eResult = AK::SoundEngine::UnloadBank( L"Desert_Footstep_bank.bnk", NULL );

이 방법에 대한 추가 정보

이 예시는 이 방법을 통해 실행할 수 있는 수많은 예시 중에서 아주 특정한 한 예시일 뿐입니다. 모든 뱅크에서 한 사운드씩, 한 이벤트씩 들어갈 내용을 개별적으로 결정할 수 있기 때문에 각 뱅크의 콘텐츠를 완전히 통제할 수 있습니다. 게임의 각 사운드마다 별도의 뱅크를 만들 수는 있지만, 이 경우 새로운 사운드가 생길 때마다 게임의 적절한 위치에 뱅크를 불러올 새로운 코드가 필요하기 때문에 유지 관리가 굉장히 어려워집니다. 각 게임은 세분성(granularity)과 게임 통합의 용이성 사이의 균형을 찾는 것을 목표로 합니다.

작은 정보: 사운드 불러오기 방식에 대해 관심이 있다면 LoadBank 대신 PrepareEvent API나 PinEventInStreamCache API, SetMedia API 사용 방법을 살펴보는 것도 좋은 방법입니다.

장점:

  • 메모리 사용량을 최적화할 수 있는 최적의 옵션입니다.
  • 게임에서 원하는 시점에 불러올 미디어를 완전히 제어할 수 있습니다.

단점:

  • 어떤 뱅크를 언제 반드시 불러와야 하는지 사운드 디자이너와 게임 프로그래머 사이에 많은 대화가 필요합니다.

방법 4: 이벤트 준비

이 방법은 다음과 같은 상황에 적용할 수 있습니다.

  • 메모리 사용량을 낮게 유지하기 위해서 아주 작은 수준의 세분성이 필요할 경우
  • 뱅크에 미디어 에셋을 분할해 넣는 것에 대해 크게 신경쓰지 않고 싶을 경우

준비된 이벤트란 정확히 무엇일까요? PrepareEvent 기능을 호출할 경우 시스템은 이벤트를 분석해서 이 이벤트에 연관된 모든 구조체와 미디어가 메모리에 로드됐는지 확인합니다. 모든 내용이 메모리에 올라가지 않았을 경우 시스템은 누락된 정보를 자동으로 디스크에서 스트리밍합니다. 이벤트는 명시적으로 준비 해제하기 전까지 항상 준비된 상태로 유지됩니다.

사용 방법:

이 방법을 사용하려면 메타데이터(구조체와 Event)를 포함하고 있는 SoundBank로부터 미디어를 명시적으로 제외시켜줘야합니다. 필요한 경우, 구조체는 별도 SoundBank의 일부가 될 수 있습니다. 그러나 구조체 로딩이 뱅크 단위로 세분화돼있기 때문에 대부분의 경우 한 SoundBank 안에 이벤트로 포함시키는 것이 적절합니다.

PrepareEvent 기능을 사용할 SoundBank를 제작할 경우, 필요한 Event와 구조체가 적어도 하나의 SoundBank에 있어야 하며 반드시 파일 시스템 내 루즈 파일(loose file)로 미디어에 접근할 수 있어야 합니다. 특정 뱅크가 참조하는 미디어가 다른 어떤 뱅크에도 포함돼있지 않은 경우, CopyStreamedFiles 도구는 이 미디어가 PrepareEvent API로 사용되며 루즈 파일을 출력 디렉터리로 복사한다고 간주합니다.

Event를 준비하기에 앞서 해당 Event는 반드시 하나의 SoundBank에서 메모리로 로드돼있어야 합니다 (LoadBank() 이용). 그 이유는 바로 이 Event가 Event를 준비할 때 필요한 의존성에 대한 정보를 담고 있기 때문입니다.

Wwise에서

  1. 'Events'라는 이름의 SoundBank를 만든 후 SoundBank Editor로 불러옵니다.
  2. 프로젝트에서 'Events' SoundBank에 몇 가지 Event를 추가하거나, 간단히 Event Work Units을 추가합니다.
  3. 'Media' 체크 상자를 선택 해제하되, 'Events'와 'Structures' 체크 상자는 선택된 상태로 유지합니다.
  4. SoundBank를 생성합니다. 루즈 미디어(loose media)를 하나 이상 참조하는 SoundBank를 생성하면 'Media referenced by <구조체> in SoundBank <뱅크> not found in any SoundBank'라는 경고 메시지가 뜹니다. 해당 메시지를 우클릭해 무시 목록에 추가할 수 있습니다.
  5. SoundBank가 참조하는 루즈 미디어는 CopyStreamedFiles 도구를 사용하는 SoundBank와 동일한 출력 디렉터리로 복사되는 것이 기본 설정으로 돼있습니다.
  6. 루즈 미디어와 함께 생성된 뱅크 폴더를 게임 애플리케이션에 복사해넣습니다.
참고: 하나의 뱅크에 담긴 구조체 데이터는 런타임 때 분할될 수 없습니다. 그렇기 때문에 AK::SoundEngine::PrepareEvent를 사용할 경우 별도의 뱅크에서 구조체 데이터가 필요하게 되면 해당 뱅크의 모든 구조체가 동시에 로드됩니다. 이러한 이유로 프로젝트의 구조체 콘텐츠를 여러 개의 뱅크로 분할함으로써 메모리에 로드되는 불필요한 정보를 최소화하는 것이 좋습니다.

게임에서

// 사운드 엔진을 초기화합니다.
AkInitSettings initSettings;
AkPlatformInitSettings platformInitSettings;
// 필요한 사항을 설정합니다.
...
// PrepareEvent와 관련된 사항을 설정합니다
initSettings.bEnableGameSyncPreparation = false; // 현재 예제에서는 사용되지 않습니다.
// 준비된 미디어가 로드될 메모리 풀을 할당합니다.
// 이를 완료하지 않으면 준비 작업이 실패합니다.
{
// (선택 사항) 메모리 풀 이름을 지정합니다. 이름을 지정하면 프로파일링 시에 유용합니다.
AK::MemoryMgr::SetPoolName( initSettings.uPrepareEventMemoryPoolID, L"PrepareEventPool" );
}
AKRESULT eResult = AK.SoundEngine.Init( initSettings, platformInitSettings );
if( eResult != AK_Success )
{
// 처리 오류
}
// Init 뱅크와 이벤트/구조체 뱅크를 불러옵니다.
AkBankID bankID; // 이 예제에서는 사용되지 않습니다.
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"Events.bnk", AK_DEFAULT_POOL_ID, bankID );
}
...
// 그런 다음, 코드 내 원하는 위치에서 다음과 같이 입력합니다.
const char * pEventsNameArray[1] = { "My_Event_Name" };
// 이벤트를 준비합니다:
eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pEventsNameArray, 1 ); // 1은 배열 크기임
// 이벤트 준비를 해제합니다:
eResult = AK::SoundEngine::PrepareEvent( Preparation_Unload, pEventsNameArray, 1 ); // 1은 배열 크기임

이 방법에 대한 추가 정보

AK::SoundEngine::PrepareEvent 호출은 반드시 I/O 함수 호출로 간주돼야 한다는 점에 주의하세요. 이전 예시에서는 차단(blocking) 기능을 사용했습니다. AK::SoundEngine::PrepareEvent 함수의 다른 오버로드를 사용해 차단하지 않는 호출로 만든 후, 별도의 콜백을 통해 완료 알림을 다시 회복시킬 수 있습니다.

장점:

  • 뱅크 생성 과정이 간단합니다.
  • 미디어의 세분성이 아주 작습니다.
  • 전반적으로 메모리 사용량이 낮게 유지됩니다.
  • 과정을 자동화하기가 쉽습니다.

단점:

  • 미디어 에셋이 하나씩 로드되면서 디스크 읽기와 탐색의 횟수가 잠재적으로 증가할 수 있습니다.
  • 총 메모리 사용량에 대한 통제력이 줄어듭니다.
  • 상호작용 음악에 가장 적합한 방법은 아닙니다.

방법 5: 이벤트와 게임 싱크 준비하기 (Switch와 State)

이 방법은 다음과 같은 상황에 적용할 수 있습니다.

  • 메모리 사용량을 낮게 유지하기 위해 미디어의 세분성이 매우 작은 단위로 필요할 경우
  • 여러 뱅크에 미디어 에셋을 분할해 넣는 것에 대해 크게 신경쓰고 싶지 않은 경우
  • 프로젝트에서 Switch나 State에 따라 여러 가지 소리를 재생하는 이벤트가 있을 경우
  • 프로젝트에서 Switch나 state에 따라 음악을 재생하는 상호작용 음악이 있을 경우

이 방법은 기본적으로 이전의 방법(방법 4: 이벤트 준비)과 동일하지만, 이벤트가 준비될 때 불러오는 미디어를 더 잘 통제할 수 있습니다. 이 방법을 사용하면 준비된 이벤트 및 현재 활성화된 게임 싱크와 관련된 미디어만 메모리에 로드됩니다.

'Play_Maincharacter_FootSteps'와 'Play_Monster_Footsteps' 이 두 이벤트가 있는 간단한 프로젝트가 있다고 가정합시다. 각 이벤트는 이동하는 캐릭터가 서있는 지표면에 따라 여러 가지 소리를 무작위로 재생하는 서로 다른 Switch Container를 재생합니다. Switch Group의 이름은 'GroundTexture'이며 'Snow', 'Concrete', 'Sand' 이 세 가지의 State를 갖고 있습니다.

Wwise에서 Switch Container의 계층 구조는 다음과 같은 형태를 이루고 있습니다.

Switch_Container_Footstep_Main_Character

  • Random_Container_Sound_Snow_main
    • Sound_Snow_main_1
    • Sound_Snow_main_2
    • Sound_Snow_main_3
  • Random_Container_Sound_Concrete_main
    • Sound_Concrete_main_1
    • Sound_Concrete_main_2
    • Sound_Concrete_main_3
  • Random_Container_Sound_Sand_main
    • Sound_Sand_main_1
    • Sound_Sand_main_2
    • Sound_Sand_main_3

그리고

Switch_Container_Footstep_Monster

  • Random_Container_Sound_Snow_Monster
    • Sound_Snow_Monster_1
    • Sound_Snow_Monster_2
    • Sound_Snow_Monster_3
  • Random_Container_Sound_Concrete_Monster
    • Sound_Concrete_Monster_1
    • Sound_Concrete_Monster_2
    • Sound_Concrete_Monster_3
  • Random_Container_Sound_Sand_main
    • Sound_Sand_Monster_1
    • Sound_Sand_Monster_2
    • Sound_Sand_Monster_3

이 예시에서는 메모리에 잠재적으로 불러올 수 있는 18개의 사운드 (3개의 사운드로 이루어진 6개의 그룹)가 있습니다.

방법 4: 이벤트 준비 를 사용해도 되지만, 그럴 경우 이벤트별로 메모리에 올라간 6개의 사운드보다 더 작은 세분성을 가질 수 없게 됩니다. 방법 3: 세부 미디어 관리 를 사용하면 보다 세밀한 세분성 단위를 가질 수는 있지만 이 간단한 프로젝트에 6개의 다른 뱅크를 만들어야 합니다 (실제 프로젝트에서 뱅크의 수는 매우 빠르게 중가합니다). 그러면 몬스터가 나타날 때 어떤 지표면을 적용할 수 있는지 확인한 다음 알맞은 뱅크를 불러와야 합니다.

반면 여기 나온 '방법 5'를 사용할 경우 어떤 이벤트와 게임 싱크가 가능한지 지정한 다음 알맞은 미디어만 불러오면 됩니다. PrepareEvent를 이용할 경우 모든 미디어는 파일 시스템에서 루즈 파일로 사용할 수 있어야 합니다.

사용 방법:

Wwise에서

  1. 'Events'라는 이름의 뱅크를 만든 후 SoundBank Editor로 불러옵니다.
  2. 이 두 이벤트를 SoundBank Editor의 Add 탭으로 드래그합니다.
  3. 'Media' 체크 상자를 선택 해제하되, 두 이벤트의 'Events'와 'Structures' 체크 상자는 선택된 상태로 유지합니다.
  4. 뱅크를 생성합니다. 루즈 미디어(loose media)를 하나 이상 참조하는 SoundBank를 생성하면 'Media referenced by <구조체> in SoundBank <뱅크> not found in any SoundBank'라는 경고 메시지가 뜹니다. 해당 메시지를 우클릭해 무시 목록에 추가할 수 있습니다.
  5. 해당 뱅크가 참조하는 루즈 미디어는 CopyStreamedFiles 도구를 사용하는 SoundBank와 동일한 출력 디렉터리로 복사되는 것이 기본 설정으로 돼있습니다.
  6. 루즈 미디어와 함께 생성된 뱅크 폴더를 게임 애플리케이션에 복사해넣습니다.

게임에서

  • 이 두 이벤트가 게임에서 필요하기 전에 'Event' SoundBank를 불러옵니다.
  • 가능한 지표면이 근처에 있을 경우 해당 게임 싱크를 활성화합니다.
  • Concrete 지표면은 항상 활성화해둡니다.
  • 주요 캐릭터의 발자국 소리 이벤트도 항상 준비해둡니다.
참고: 하나의 뱅크에 담긴 구조체 데이터는 런타임 때 분할될 수 없습니다. 그렇기 때문에 AK::SoundEngine::PrepareEvent를 사용할 경우 별도의 뱅크에서 구조체 데이터가 필요하게 되면 해당 뱅크의 모든 구조체가 동시에 로드됩니다. 이러한 이유로 프로젝트의 구조체 콘텐츠를 여러 개의 뱅크로 분할함으로써 메모리에 로드되는 불필요한 정보를 최소화하는 것이 좋습니다.
// 사운드 엔진을 초기화합니다.
AkInitSettings initSettings;
AkPlatformInitSettings platformInitSettings;
// 필요한 사항을 설정합니다.
...
// PrepareEvent와 관련된 사항을 설정합니다.
////////////////////////////////////////////////////////////////
// bEnableGameSyncPreparation 플래그가 true로 설정되고
// 게임 싱크 매커니즘을 활성화합니다. false로 설정되면,
// 모든 게임 싱크에 연결된 미디어를 불러오며
// AK::SoundEngine:PrepareGameSyncs를 호출하지 않아도 됩니다.
//
// true로 설정되면, AK::SoundEngine:PrepareGameSyncs 호출로 게임 싱크가 활성화된 경우를 제외하고는
// 게임 싱크에 의존하는 미디어가 로드되지 않습니다.
////////////////////////////////////////////////////////////////
initSettings.bEnableGameSyncPreparation = true;
// 준비된 미디어가 로드될 메모리 풀을 할당합니다.
// 이를 완료하지 않으면 준비 작업이 실패합니다.
{
// (선택 사항) 메모리 풀 이름을 지정합니다. 이름을 지정하면 프로파일링 시에 유용합니다.
AK::MemoryMgr::SetPoolName( initSettings.uPrepareEventMemoryPoolID, L"PrepareEventPool" );
}
AKRESULT eResult = AK.SoundEngine.Init( initSettings, platformInitSettings );
if( eResult != AK_Success )
{
// 처리 오류
}
// Init 뱅크과 이벤트/구조체 뱅크를 불러옵니다.
AkBankID bankID; // 이 예제에서는 사용되지 않습니다.
AKRESULT eResult = AK::SoundEngine::LoadBank( L"Init.bnk", AK_DEFAULT_POOL_ID, bankID );
if( eResult == AK_Success )
{
eResult = AK::SoundEngine::LoadBank( L"Events.bnk", AK_DEFAULT_POOL_ID, bankID );
}
// ... 이 시점에서는,
// 두 이벤트가 로드되어있지만 준비되지는 않았습니다. 현재 불러온 미디어가 없습니다.
const char * pNameArray[1];
// 주요 캐릭터 발자국 소리 이벤트를 준비합니다.
pNameArray[0] = "Play_Maincharacter_FootSteps";
eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pNameArray, 1 ); // 1은 배열 크기임
// ... 이 시점에서는,
// 한 이벤트가 준비됐지만 아직 미디어는 불러오지 않았습니다.
// 이제 콘크리트 지표면은 게임에서 항상 사용 가능하기 때문에 다음을 처리합니다.
pNameArray[0] = "Concrete";
eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Load, in_eType, "GroundTexture", pNameArray, 1 );
// ... 이 시점에서는,
// 3개의 사운드 Sound_Concrete_main_1과 Sound_Concrete_main_2, Sound_Concrete_main_3가 로드됐습니다.
// 이제 주요 캐릭터가 눈이 덮인 지표면으로 들어간다고 가정합시다.
pNameArray[0] = "Snow";
eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Load, in_eType, "GroundTexture", pNameArray, 1 );
// ... 이 시점에서,
// 3개의 사운드, Sound_Snow_main_1, Sound_Snow_main_2 and Sound_Snow_main_3가 방금 로드됐습니다.
// 그런 다음 갑자기 Monster가 나타난다고 가정합시다.
pNameArray[0] = "Play_Monster_Footsteps";
eResult = AK::SoundEngine::PrepareEvent( Preparation_Load, pEventsNameArray, 1 ); // 1은 배열 크기임
// ... 이 시점에서,
// 6개의 사운드( Sound_Concrete_Monster_1,2,3과 Sound_Snow_Monster_1,2,3 )가 추가로 로드됩니다.
// 그러면 플레이어는 몬스터로부터 피해 달아나고 몬스터는 플레이어를 뒤쫓아옵니다.
// 플레이어와 몬스터는 멀리 달려가 더 이상 눈이 없는 곳까지 가게 됩니다.
pNameArray[0] = "Snow";
eResult = AK::SoundEngine::PrepareGameSyncs( Preparation_Unload, in_eType, "GroundTexture", pNameArray, 1 );
// ... 이 시점에서,
// 눈과 관련된 6개의 사운드( Sound_Snow_Monster_1.2.3 and Sound_Snow_main_1.2.3 )가 메모리에서 언로드됩니다.
...
참고: AK::SoundEngine::PrpareEvent와 AK::SoundEngine::PrepareGameSync 중 어느 것을 먼저 호출하는지는 중요하지 않습니다. State가 바뀔 때마다 이벤트와 게임 싱크를 서로 맞춰보고 이에 따라 미디어 풀이 업데이트됩니다.

이 방법에 대한 추가 정보

장점:

  • 뱅크 생성 과정이 간단합니다.
  • 미디어의 세분성이 아주 작습니다.
  • 전반적으로 메모리 사용량이 낮게 유지됩니다.
  • 과정을 자동화하기가 쉽습니다.
  • 유용한 미디어만 불러옵니다.

단점:

  • 미디어 에셋이 하나씩 로드되면서 디스크 읽기와 탐색의 횟수가 잠재적으로 증가할 수 있습니다.
  • 총 메모리 사용량에 대한 통제력이 줄어듭니다.
  • 새로운 데이터를 불러와야 하는 수많은 이벤트가 준비될 경우 게임 싱크를 활성화하면 스트리밍 대역폭이 높아질 수 있습니다.
참고
AKSOUNDENGINE_API AKRESULT PrepareEvent(PreparationType in_PreparationType, const char **in_ppszString, AkUInt32 in_uNumEvent)
Audiokinetic namespace
AKSOUNDENGINE_API AKRESULT PrepareGameSyncs(PreparationType in_PreparationType, AkGroupType in_eGameSyncType, const char *in_pszGroupName, const char **in_ppszGameSyncName, AkUInt32 in_uNumGameSyncs)
AKSOUNDENGINE_API AKRESULT SetPoolName(AkMemPoolId in_poolId, const char *in_pszPoolName)
AKSOUNDENGINE_API void GetDefaultInitSettings(AkInitSettings &out_settings)
AkMemPoolId uPrepareEventMemoryPoolID
Memory pool where data allocated by AK::SoundEngine::PrepareEvent() and AK::SoundEngine::PrepareGameS...
Definition: AkSoundEngine.h:214
AKSOUNDENGINE_API AKRESULT UnloadBank(const char *in_pszString, const void *in_pInMemoryBankPtr, AkMemPoolId *out_pMemPoolId=NULL)
AKSOUNDENGINE_API void GetDefaultPlatformInitSettings(AkPlatformInitSettings &out_platformSettings)
AKSOUNDENGINE_API AKRESULT LoadBank(const char *in_pszString, AkMemPoolId in_memPoolId, AkBankID &out_bankID)

이 페이지가 도움이 되었나요?

지원이 필요하신가요?

질문이 있으신가요? 문제를 겪고 계신가요? 더 많은 정보가 필요하신가요? 저희에게 문의해주시면 도와드리겠습니다!

지원 페이지를 방문해 주세요

작업하는 프로젝트에 대해 알려주세요. 언제든지 도와드릴 준비가 되어 있습니다.

프로젝트를 등록하세요. 아무런 조건이나 의무 사항 없이 빠른 시작을 도와드리겠습니다.

Wwise를 시작해 보세요