icon안동민 개발노트

사운드 큐 및 사운드 웨이브 기본


 언리얼 엔진의 오디오 시스템은 게임 내 사운드를 효과적으로 관리하고 재생할 수 있는 강력한 도구를 제공합니다.

 이 절에서는 C++를 사용하여 언리얼 엔진의 오디오 시스템을 다루는 방법을 살펴보겠습니다.

USoundWave와 USoundCue의 차이점 및 사용법

 USoundWave는 단일 오디오 파일을 나타내는 반면, USoundCue는 여러 사운드와 효과를 조합할 수 있는 노드 기반 에디터를 제공합니다.

// USoundWave 사용 예
UPROPERTY(EditAnywhere, Category = "Audio")
USoundWave* SimpleSoundEffect;
 
// USoundCue 사용 예
UPROPERTY(EditAnywhere, Category = "Audio")
USoundCue* ComplexSoundEffect;

C++에서 사운드 에셋 로드 및 재생

 사운드 에셋을 동적으로 로드하고 재생하는 방법:

##include "Sound/SoundWave.h"
##include "Kismet/GameplayStatics.h"
 
void AMyActor::PlaySound()
{
    if (SimpleSoundEffect)
    {
        UGameplayStatics::PlaySound2D(this, SimpleSoundEffect);
    }
}
 
void AMyActor::LoadAndPlaySound(FString SoundPath)
{
    USoundWave* LoadedSound = LoadObject<USoundWave>(nullptr, *SoundPath);
    if (LoadedSound)
    {
        UGameplayStatics::PlaySound2D(this, LoadedSound);
    }
}

사운드 큐 내의 다양한 노드 프로그래밍 방식 제어

 사운드 큐 내의 노드를 C++에서 제어하는 방법

##include "Sound/SoundCue.h"
##include "Sound/SoundNodeModulator.h"
 
void AMyActor::ModifySoundCue(USoundCue* SoundCue, float PitchMultiplier)
{
    if (SoundCue)
    {
        TArray<USoundNode*> AllNodes;
        SoundCue->GetAllNodes(AllNodes);
 
        for (USoundNode* Node : AllNodes)
        {
            if (USoundNodeModulator* ModulatorNode = Cast<USoundNodeModulator>(Node))
            {
                ModulatorNode->PitchModulation.Min = PitchMultiplier;
                ModulatorNode->PitchModulation.Max = PitchMultiplier;
            }
        }
 
        SoundCue->MarkPackageDirty();
    }
}

동적 사운드 파라미터 조절

 재생 중인 사운드의 파라미터를 동적으로 조절하는 방법

##include "Components/AudioComponent.h"
 
UPROPERTY()
UAudioComponent* AudioComp;
 
void AMyActor::StartDynamicSound()
{
    if (ComplexSoundEffect)
    {
        AudioComp = UGameplayStatics::SpawnSound2D(this, ComplexSoundEffect);
        if (AudioComp)
        {
            AudioComp->SetFloatParameter(FName("Volume"), 0.5f);
        }
    }
}
 
void AMyActor::UpdateSoundParameter(float NewVolume)
{
    if (AudioComp && AudioComp->IsPlaying())
    {
        AudioComp->SetFloatParameter(FName("Volume"), NewVolume);
    }
}

3D 사운드 구현

 3D 공간에서 사운드를 재생하고 위치를 업데이트하는 방법

void AMyActor::Play3DSound(FVector SoundLocation)
{
    if (ComplexSoundEffect)
    {
        AudioComp = UGameplayStatics::SpawnSoundAtLocation(this, ComplexSoundEffect, SoundLocation);
    }
}
 
void AMyActor::Update3DSoundLocation(FVector NewLocation)
{
    if (AudioComp)
    {
        AudioComp->SetWorldLocation(NewLocation);
    }
}

사운드 페이드 인 / 아웃 효과 구현

 사운드를 부드럽게 페이드 인 / 아웃하는 방법

void AMyActor::FadeInSound(float FadeDuration)
{
    if (AudioComp)
    {
        AudioComp->FadeIn(FadeDuration);
    }
}
 
void AMyActor::FadeOutSound(float FadeDuration)
{
    if (AudioComp)
    {
        AudioComp->FadeOut(FadeDuration, 0.0f);
    }
}

사운드 시스템의 성능 최적화 전략

  1. 사운드 풀링 시스템 구현
  2. 거리 기반 사운드 컬링
  3. 오디오 스트리밍 활용
UCLASS()
class MYGAME_API USoundPoolManager : public UObject
{
    GENERATED_BODY()
 
public:
    UAudioComponent* GetPooledSound(USoundBase* Sound);
    void ReturnSoundToPool(UAudioComponent* AudioComp);
 
private:
    TMap<USoundBase*, TArray<UAudioComponent*>> SoundPool;
};
 
UAudioComponent* USoundPoolManager::GetPooledSound(USoundBase* Sound)
{
    if (SoundPool.Contains(Sound) && SoundPool[Sound].Num() > 0)
    {
        return SoundPool[Sound].Pop();
    }
    else
    {
        return UGameplayStatics::SpawnSound2D(this, Sound, 0.0f, 1.0f, 0.0f);
    }
}
 
void USoundPoolManager::ReturnSoundToPool(UAudioComponent* AudioComp)
{
    if (AudioComp && AudioComp->Sound)
    {
        AudioComp->Stop();
        SoundPool.FindOrAdd(AudioComp->Sound).Add(AudioComp);
    }
}

대규모 게임에서의 효율적인 오디오 관리

  1. 오디오 에셋 카테고리화 및 프리로딩
  2. 동적 오디오 믹싱 시스템 구현
  3. 메모리 사용량 모니터링 및 최적화
UCLASS()
class MYGAME_API UAudioManager : public UObject
{
    GENERATED_BODY()
 
public:
    void PreloadAudioCategory(FName CategoryName);
    void UnloadAudioCategory(FName CategoryName);
    void SetCategoryVolume(FName CategoryName, float Volume);
 
private:
    TMap<FName, TArray<USoundBase*>> AudioCategories;
    TMap<FName, float> CategoryVolumes;
};
 
void UAudioManager::PreloadAudioCategory(FName CategoryName)
{
    // 카테고리에 속한 오디오 에셋 로드
}
 
void UAudioManager::SetCategoryVolume(FName CategoryName, float Volume)
{
    CategoryVolumes.Add(CategoryName, Volume);
    // 카테고리에 속한 모든 활성 사운드의 볼륨 조정
}

플랫폼별 오디오 시스템 차이에 대한 대응

 다양한 플랫폼에 대응하기 위한 전략

  1. 플랫폼별 오디오 설정 관리
  2. 동적 샘플레이트 및 채널 수 조정
  3. 플랫폼 특화 코덱 사용
void AMyGameMode::InitializeAudioSettings()
{
    FString PlatformName = UGameplayStatics::GetPlatformName();
    
    if (PlatformName == "Windows")
    {
        // Windows 특화 오디오 설정
    }
    else if (PlatformName == "Android" || PlatformName == "IOS")
    {
        // 모바일 플랫폼 특화 오디오 설정
    }
    // 기타 플랫폼에 대한 처리
}

 언리얼 엔진의 오디오 시스템을 C++로 다루면 게임 내 사운드를 세밀하게 제어하고 최적화할 수 있습니다. USoundWaveUSoundCue를 적절히 활용하여 단순한 효과음부터 복잡한 사운드 시퀀스까지 다양한 오디오 요소를 구현할 수 있습니다.

 동적 사운드 파라미터 조절, 3D 사운드 구현, 페이드 효과 등을 통해 더욱 풍부한 오디오 경험을 제공할 수 있습니다. 사운드 풀링, 거리 기반 컬링, 오디오 스트리밍 등의 최적화 기법을 적용하여 대규모 게임에서도 효율적인 오디오 시스템을 구축할 수 있습니다.

 플랫폼별 차이를 고려한 오디오 시스템 설계는 다양한 디바이스에서 일관된 사운드 품질을 보장하는 데 중요합니다. 각 플랫폼의 특성을 고려하여 오디오 설정을 조정하고, 필요에 따라 다른 코덱이나 압축 방식을 사용하는 것이 좋습니다.

 마지막으로, 오디오 시스템의 성능과 메모리 사용량을 지속적으로 모니터링하고 최적화하는 것이 중요합니다. 프로파일링 도구를 활용하여 병목 현상을 식별하고, 필요에 따라 동적으로 오디오 품질을 조정하는 시스템을 구현하면 더욱 안정적이고 효율적인 게임 오디오 시스템을 만들 수 있습니다.