음악 상태 머신
게임 상태와 음악 상태를 연결하는 상태 머신 설계 및 Blueprint/C++ 구현
음악 상태 머신 설계
게임 상태와 음악 상태를 매핑하는 아키텍처
음악 상태 머신은 게임의 상태(탐험, 전투, 대화 등)를 음악 상태에 매핑하고, 상태 전환 시 적절한 트랜지션을 수행하는 시스템입니다.
↓ ↓ ↓
상태 전환 매트릭스
| From \ To | Exploration | Combat | Stealth | Boss |
|---|---|---|---|---|
| Exploration | - | 마디 경계 + 스팅어 | 페이드 2초 | 스팅어 + 즉시 |
| Combat | 마디 경계 + 페이드 | - | 마디 경계 | 스팅어 + 크로스페이드 |
| Stealth | 페이드 3초 | 스팅어 + 즉시 | - | 스팅어 + 즉시 |
| Boss | Victory 전환 구간 | - | - | - |
C++ Music Manager 구현
음악 상태 머신의 C++ 핵심 구현
// 음악 상태 열거형
UENUM(BlueprintType)
enum class EMusicState : uint8
{
None,
Exploration,
Combat,
Stealth,
Boss,
Victory,
Defeat
};
// 음악 상태 데이터
USTRUCT(BlueprintType)
struct FMusicStateData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
TArray<USoundBase*> Stems;
UPROPERTY(EditAnywhere)
float BPM = 120.0f;
UPROPERTY(EditAnywhere)
TArray<float> DefaultStemVolumes;
};
// 상태 전환 함수
void UMusicManager::TransitionToState(
EMusicState NewState)
{
if (NewState == CurrentState) return;
EMusicState OldState = CurrentState;
CurrentState = NewState;
// 상태별 트랜지션 로직
const FMusicStateData& NewData =
MusicStateMap[NewState];
// 1) Quartz 클럭의 다음 마디에 맞춰 전환 예약
FQuartzQuantizationBoundary Boundary;
Boundary.Quantization =
EQuartzCommandQuantization::Bar;
// 2) 현재 스템 페이드 아웃
for (UAudioComponent* Stem : ActiveStems)
{
Stem->FadeOut(1.0f, 0.0f);
}
// 3) 새 스템을 양자화 재생
ActiveStems.Empty();
for (int32 i = 0; i < NewData.Stems.Num(); ++i)
{
UAudioComponent* StemComp =
UGameplayStatics::SpawnSound2D(
this, NewData.Stems[i]);
StemComp->SetVolumeMultiplier(
NewData.DefaultStemVolumes[i]);
StemComp->PlayQuantized(
GetWorld(), ClockHandle, Boundary,
FOnQuartzCommandEventBP());
ActiveStems.Add(StemComp);
}
}
게임플레이 연동
게임 이벤트로 음악 상태 전환을 트리거하기
// 전투 시스템에서 음악 상태 변경
void ACombatManager::OnCombatStarted()
{
MusicManager->TransitionToState(
EMusicState::Combat);
MusicManager->PlayStinger(
EMusicStingerType::EnemyDetected);
}
void ACombatManager::OnCombatEnded()
{
// 전투 종료 후 일정 딜레이를 두고 탐험 음악으로 복귀
GetWorldTimerManager().SetTimer(
MusicTransitionTimer,
[this]()
{
MusicManager->TransitionToState(
EMusicState::Exploration);
},
3.0f, // 3초 후 전환
false
);
}
// 보스전 진입
void ABossArena::OnBossEncounterBegin()
{
MusicManager->TransitionToState(
EMusicState::Boss);
MusicManager->PlayStinger(
EMusicStingerType::BossAppear);
}
// 보스 처치
void ABossArena::OnBossDefeated()
{
MusicManager->TransitionToState(
EMusicState::Victory);
}
단순한 Combat/Explore 이진 분류보다, 전투 강도(0.0~1.0)를 실시간으로 계산하여 스템 볼륨에 반영하면 더 세밀한 적응형 음악이 됩니다. 적 수, 거리, 체력 비율 등을 종합하여 Intensity를 계산하세요.
MetaSounds와 Quartz 연동
MetaSounds 내부에서 Quartz 클럭 활용하기
MetaSounds는 Quartz와의 직접 연동을 지원합니다. MetaSound 그래프 내부에서 Quartz 이벤트를 수신하고, 비트에 동기화된 절차적 음악을 생성할 수 있습니다.
MetaSounds의 오실레이터 + Quartz 비트 동기화를 결합하면, 코드 없이 에디터에서 절차적 음악을 디자인할 수 있습니다. 아르페지에이터, 시퀀서, 드럼 머신 등을 MetaSound 그래프로 구현하는 것이 가능합니다.
핵심 요약
- 음악 상태 머신으로 게임 상태(탐험, 전투 등)와 음악 상태를 체계적으로 매핑한다
- 상태 전환 매트릭스로 각 전환에 적합한 트랜지션 방식을 정의한다
- 게임 이벤트(전투 시작, 보스 등장 등)에 연동하여 자연스러운 음악 전환을 구현한다
- MetaSounds와 Quartz를 결합하여 절차적 음악(아르페지에이터, 시퀀서)을 구현할 수 있다
- 전투 강도 값을 실시간으로 계산하여 스템 볼륨에 반영하면 더 세밀한 적응형 음악이 된다
도전 과제
배운 내용을 직접 실습해보세요
Enum으로 음악 상태(Explore, Combat, Stealth, Boss)를 정의하고, 상태 전환 시 적절한 음악 트랙을 크로스페이드로 전환하는 블루프린트 시스템을 구현하세요. FadeIn/FadeOut 시간은 상태별로 다르게 설정하세요.
전투 인텐시티(0.0~1.0)를 계산하는 시스템을 만들고, 인텐시티 구간별(Low, Medium, High)로 음악 레이어를 추가/제거하세요. 적 수, 거리, 플레이어 체력을 종합하여 인텐시티를 산출하세요.
Data Table로 음악 상태 전환 규칙(소스 상태, 대상 상태, 크로스페이드 시간, 스팅거 에셋, 전환 조건)을 정의하고, 이를 기반으로 동작하는 데이터 드리븐 음악 매니저를 구현하세요.