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 사운드 구현, 페이드 효과 등을 통해 더욱 풍부한 오디오 경험을 제공할 수 있습니다.

 사운드 풀링, 거리 기반 컬링, 오디오 스트리밍 등의 최적화 기법을 적용하여 대규모 게임에서도 효율적인 오디오 시스템을 구축할 수 있습니다.