2026. 5. 20. 21:34ㆍ언리얼 7기 본캠프
오늘은 최종 팀 프로젝트에서 채보 편집기와 Wwise를 어떻게 연결할지 정리해봤음
이번에 Wwise를 프로젝트에 붙이고 관련 파일들을 정리하면서 느낀 건, Wwise 연동은 그냥 플러그인 설치하고 Event를 호출하는 문제가 아니었다는 점임
특히 우리 프로젝트는 일반적인 리듬게임 하나를 만드는 게 아니라, 여러 미니게임이 같은 채보 포맷을 공유하는 구조라서 채보와 사운드를 어떻게 분리할지가 중요했음
채보는 언제 어떤 액션이 발생하는지 알려주는 데이터고, Wwise는 그 액션과 판정 결과가 어떤 소리로 들릴지를 담당하는 오디오 레이어라고 보는 게 맞았음
오늘 작업한 내용
오늘 작업하면서 크게 세 가지를 정리했음
1. Wwise 플러그인 파일을 팀 프로젝트에서 정상적으로 관리할 수 있게 설정
2. 채보 편집기에서 저장하는 JSON 메타데이터 구조 정리
3. 채보 데이터와 Wwise Event를 Unreal에서 어떻게 연결할지 구상
Wwise를 붙이면서 생각보다 Git 설정에서 신경쓸 게 많았음
Unreal 프로젝트 기본 gitignore 규칙에는 빌드 결과물이나 바이너리 파일을 무시하는 규칙이 많은데, Wwise 플러그인은 ThirdParty 쪽에 dll, lib, pdb 같은 SDK 파일들이 같이 들어감
문제는 이 파일들이 빠지면 내 PC에서는 열리는데 팀원 PC에서는 빌드가 깨질 수 있다는 점이었음
그래서 Wwise ThirdParty 파일이 ignore되지 않도록 예외를 열어두고, 바이너리 payload는 LFS로 관리하는 방향으로 정리했음
이 작업을 하면서 느낀 건 Wwise 연동은 기능 구현 이전에 팀원들이 같은 환경을 받을 수 있게 만드는 게 먼저라는 점이었음
채보 편집기에서 저장해야 하는 정보
이번 채보 편집기는 단순히 노트를 찍는 툴이 아니라, Unreal과 Wwise가 같이 쓸 메타데이터까지 저장하는 방향으로 잡았음
메타데이터에는 이런 값들이 들어감
chartVersion
difficulty
chartId
miniGameId
songId
bpm
offsetMs
wwise.bankName
wwise.bgmEventName
미니게임 선택 화면에서 특정 채보를 골랐을 때, Unreal은 이 채보가 어떤 미니게임인지, 어떤 곡인지, BPM이 몇인지, offset이 얼마인지, 어떤 Wwise BGM Event를 틀어야 하는지 알아야 함
그래서 채보 파일은 단순 노트 리스트가 아니라 라운드 시작에 필요한 기본 정보를 들고 있는 데이터가 되어야 했음
저장 구조는 이런 느낌으로 진행됨
{
"chartVersion": 1,
"chartId": "JumpJump_Standard_001",
"miniGameId": "JumpJump",
"songId": "Song_JumpJump_001",
"difficulty": "Standard",
"bpm": 120,
"offsetMs": 0,
"wwise": {
"bankName": "Bank_JumpJump",
"bgmEventName": "Play_BGM_JumpJump"
},
"noteFormat": ["id", "timeMs", "beat", "action", "lane", "type"],
"notes": [
[1, 1000, 2, "A", 0, "Tap"]
]
}
여기서 중요한 건 wwise.bankName과 wwise.bgmEventName임
채보가 직접 Wwise Event Asset을 들고 있는 건 아니지만, 어떤 Bank와 어떤 BGM Event를 사용해야 하는지는 알려줘야 함
즉 채보는 Wwise를 직접 실행하지 않고, Wwise를 찾기 위한 이름만 들고 있는 구조임
이렇게 해야 JSON 파일은 가볍게 유지하고, 실제 Unreal 에셋 참조는 Unreal 쪽에서 관리할 수 있음
채보 편집기에서 noteFormat은 이 형태로 고정했음
["id", "timeMs", "beat", "action", "lane", "type"]
beat
- 에디터에서 박자 기준으로 노트를 배치하기 좋음
- 1/4, 1/8, 1/16, 1/32 snap과 잘 맞음
- 마디와 박자를 디버깅하기 좋음
timeMs
- Unreal에서 입력 판정 계산에 바로 쓰기 좋음
- offsetMs 적용 결과를 확인하기 좋음
- 실제 플레이 시간과 비교하기 좋음
A / B / C / D / E 액션과 미니게임의 관계
우리 프로젝트의 핵심은 모든 미니게임이 같은 채보 포맷을 공유한다는 점임
그래서 채보에는 구체적인 행동 이름을 박아두는 게 아니라 A / B / C / D / E 같은 공통 액션만 저장함
A
B
C
D
E
그리고 각 미니게임이 이 액션을 자기 방식으로 해석함(아래는 예제로서 최종본이 아님)
점프점프
A : 왼쪽 캐릭터 착지
B : 가운데 캐릭터 착지
C : 오른쪽 캐릭터 착지
뒤뚱뒤뚱
A : 왼발
B : 오른발
C : 왼발
D : 오른발
주사위 굴리기
A : 왼쪽
B : 위
C : 오른쪽
D : 아래
따라쟁이 동물
A : 동작 1
B : 동작 2
C : 동작 3
D : 동작 4
이 구조에서 채보가 Play_Jump_Land 같은 Wwise Event를 직접 들고 있으면 문제가 생김
같은 A 액션이라도 미니게임마다 의미가 달라지기 때문임
그래서 채보는 액션 타이밍만 관리하고, 실제 사운드는 미니게임과 AudioMap 쪽에서 결정해야 함
정리하면 이렇게 됨
채보
- 몇 ms에 A 액션이 발생하는지만 저장
미니게임
- A 액션을 자기 규칙에 맞게 해석
판정 시스템
- 입력 시간과 액션 타이밍을 비교
Wwise
- 판정 결과와 미니게임 상태에 맞는 사운드 재생
이렇게 해야 채보 포맷을 모든 미니게임에서 공유할 수 있음
Unreal 구조체와 연결하기
현재 프로젝트 구조에서는 FPTBChartData와 FPTBNoteEvent가 이 역할을 맡게 될 것 같음
FPTBChartData는 채보 전체 정보를 들고 있음
FName ChartId;
FName SongId;
FName MiniGameId;
EPTBDifficulty Difficulty;
float BPM;
float OffsetMs;
float SongLengthMs;
FName WwiseEventName;
FName WwiseBankName;
이 구조를 보면 채보 메타데이터와 거의 1대1로 연결됨
chartId -> ChartId
songId -> SongId
miniGameId -> MiniGameId
difficulty -> Difficulty
bpm -> BPM
offsetMs -> OffsetMs
wwise.bankName -> WwiseBankName
wwise.bgmEventName -> WwiseEventName
FPTBNoteEvent는 노트 하나의 정보를 들고 있음
int32 NoteId;
float BeatTime;
float TimeMs;
EPTBActionType ActionType;
bool bIsLongNote;
float DurationBeat;
FName SectionName;
TMap<FName, FString> Payload;
여기서 ActionType은 A / B / C / D / E를 Unreal enum으로 변환한 값이 됨
채보 편집기에서 저장한 A는 ActionA, B는 ActionB로 바뀌어야 함
A -> ActionA
B -> ActionB
C -> ActionC
D -> ActionD
E -> ActionE
이 매핑은 JSON 파서에서 확실하게 처리해야 할 것 같음
만약 이 부분이 틀어지면 편집기에서는 A를 찍었는데 게임에서는 B 액션이 나가는 식의 문제가 생길 수 있음
Wwise EventMap을 따로 두는 이유
채보에 Wwise Event 이름을 넣긴 하지만, 실제 UAkAudioEvent를 채보가 직접 들고 있으면 안 된다고 봤음
JSON은 외부 파일이고, Unreal의 에셋 참조는 Unreal 안에서 관리하는 게 맞음
그래서 UPTBWwiseEventMapAsset 같은 데이터 에셋이 필요함
역할은 이렇게 나눌 수 있음
BGMEvents
- 미니게임별 BGM Event
JudgeEventMap
- HighPerfect / Perfect / Good / Miss 판정 사운드
UIEvents
- 선택 / 확인 / 취소 / 결과창 같은 UI 사운드
MiniGameSFXMap
- 점프 착지음, 주사위 굴림음, 낚시 릴 감는 소리 같은 미니게임 전용 사운드
이렇게 두면 채보 파일은 이름만 알고 있고, 실제 Wwise Event Asset은 EventMap에서 찾아서 재생할 수 있음
흐름은 이런 식으로 생각함
.rhythmc 로드
FPTBChartData 생성
WwiseBankName / WwiseEventName 확인
EventMapAsset에서 BGM Event 검색
BGM 재생
PlayingId를 Conductor에 전달
Conductor가 채보 진행
판정 결과에 따라 JudgeEventMap에서 SFX 재생
이 구조가 마음에 들었던 이유는 채보와 사운드 에셋의 결합을 줄일 수 있기 때문임
채보를 만든 사람이 Wwise 에셋 경로까지 알 필요는 없음
대신 정해진 이름 규칙만 맞추면 됨
Conductor가 맡아야 하는 역할
채보와 Wwise 사이에서 가장 중요한 클래스는 Conductor가 될 것임
Conductor는 단순히 노트를 하나씩 꺼내는 클래스가 아니라, 현재 음악 시간과 채보 이벤트를 맞춰서 게임 전체에 알려주는 역할임
필요한 기능은 이 정도라고 봄
채보 데이터 저장
BGM PlayingId 저장
현재 음악 시간 계산
현재 Beat 계산
LookAheadBeats 기준으로 미리보기 큐 발행
판정 타이밍에 NoteEvent 발행
곡 종료 시 OnChartEnd 호출
여기서 LookAheadBeats가 중요함
우리 미니게임들은 노트가 판정선에 닿는 순간만 중요한 게 아니라, 그 전에 미리 보여줘야 하는 연출이 있음
예를 들면 점프점프는 캐릭터가 먼저 점프하고 착지 타이밍을 맞춰야 하고, 뒤뚱뒤뚱은 발자국이나 걷는 모션이 먼저 보여야 하는 등의 연출이 필요함
그래서 Conductor는 두 종류의 이벤트를 던지는 게 좋아보임
OnNoteCue
- 미리 보여줄 연출용 이벤트
OnNoteEvent
- 실제 판정 타이밍 이벤트
이렇게 나누면 미니게임 쪽에서 연출과 판정을 분리해서 처리할 수 있음
판정 시스템과 Wwise의 관계
현재 판정 기준은 이렇게 잡혀 있음
HighPerfect : 21ms
Perfect : 50ms
Good : 70ms
Miss : 120ms
흐름은 이렇게 가야 함
플레이어 입력
JudgementSystem이 입력 시간과 노트 시간 비교
FPTBJudgementResult 생성
판정 타입에 따라 JudgeEventMap에서 Wwise Event 검색
Perfect / Good / Miss 사운드 재생
미니게임별 성공 / 실패 연출 실행
이렇게 하면 판정 로직과 오디오 연출이 분리됨
판정 윈도우를 조정해도 Wwise Event 구조를 바꿀 필요가 없고, 사운드가 바뀌어도 판정 로직이 흔들리지 않음
지금 기준으로 잡은 흐름
현재 기준으로는 전체 흐름을 이렇게 잡고 있음
1. 채보 편집기에서 음악을 열고 BPM / Offset을 맞춤
2. A / B / C / D / E 액션을 beat 기준으로 찍음
3. chartId, miniGameId, songId, wwise.bankName, wwise.bgmEventName을 입력함
4. .rhythmc JSON으로 저장함
5. Unreal에서 JSON을 읽어 FPTBChartData와 FPTBNoteEvent 배열로 변환함
6. FPTBChartData의 WwiseEventName으로 BGM Event를 찾음
7. Wwise BGM을 재생하고 PlayingId를 Conductor에 넘김
8. Conductor가 현재 시간 기준으로 OnNoteCue와 OnNoteEvent를 발행함
9. JudgementSystem이 입력을 판정함
10. 판정 결과에 따라 Wwise Judge SFX를 재생함
'언리얼 7기 본캠프' 카테고리의 다른 글
| 260601 TIL - 미니게임 베이스를 만드는것과 고충들 (0) | 2026.06.01 |
|---|---|
| 260515 TIL - UE 5.6과 리듬게임 프로젝트, 그리고 Wwise (0) | 2026.05.18 |
| 260508 TIL - QA 프로젝트 후기 (0) | 2026.05.08 |
| 260507 TIL - 양과 늑대 문제와 이진트리 백트래킹 최적화 (0) | 2026.05.07 |
| 260501 TIL - 챕터5 1주차 (0) | 2026.05.01 |