📌 오늘의 요약
단일 맵 전투 시스템 최적화, 체크포인트 부활 시 발생하는 프레임 멈춤(Lag) 개선, 다수 몬스터 전투 중 레벨 전환 시 발생하는 오디오 크래시 등 총 5가지 핵심 이슈의 원인을 분석하고 C++로 해결했다.
🛠️ 주요 트러블슈팅 및 구현 내용
1. ⚔️ 단일 맵 구조 정돈 & 몬스터 자동 감지 승리 시스템
- 🔴 문제 상황: 기존 멀티 스테이지 전용 잔여 코드(CurrentStage 등)가 남아있어 불필요한 태그 검사가 실행되고 로직이 꼬이는 현상 발생.
- 🟢 해결 방법:
- 쓰이지 않는 스테이지 이동/추적 로직을 C++(J_GameMode.cpp)에서 완전히 제거했다.
- BeginPlay 시점에 맵에 스폰된 몬스터 수를 자동 스캔(GetAllActorsOfClass)하여 목표 점수(TargetScore)로 자동 지정하도록 수정했다.
- 몬스터 처치 시 KillCount >= TargetScore 조건을 만족하면 바로 승리 처리(HandleVictory)되도록 구조를 단순화했다.
2. 🦘 Enhanced Input 점프 꾹 누름 시 공중 부양 버그 픽스
- 🔴 문제 상황: 점프키(Space)를 꾹 누르고 있으면 캐릭터가 바닥으로 떨어지지 않고 계속 점프하여 공중 부양하는 현상 발생.
- 🔍 원인 분석: 점프 입력 바인딩이 ETriggerEvent::Triggered(매 프레임 호출)로 설정되어 있어, 키를 홀드하는 동안 매 프레임마다 상향 가속도가 지속 주입되었다.
- 🟢 해결 코드: 입력 이벤트를 Started로 변경하여 첫 프레임 1회만 점프가 실행되도록 수정하고 불필요한 조건문을 제거했다.
// ETriggerEvent::Triggered ➔ ETriggerEvent::Started 로 변경
EnhancedInput->BindAction(PlayerController->JumpAction, ETriggerEvent::Started, this, &AJ_PlayerCharacter::StartJump);
void AJ_PlayerCharacter::StartJump(const FInputActionValue& value)
{
if (bIsDead || bIsRolling || bIsUsingSkill) return;
Jump(); // 조건문 없이 1회 즉시 점프
}
3. 🚀 체크포인트 무로딩(In-Memory) 부활 시스템 구현
- 🔴 문제 상황: 체크포인트 부활 시 UGameplayStatics::OpenLevel을 사용해 맵을 새로고침하면서, 3D 에셋 디스크 동기 읽기 및 메모리 정리(GC)로 인해 약 1초간 화면이 멈추는 프레임 히치(Lag) 발생. 또한 레벨 재로드 후 메인메뉴 UI가 겹치거나 즉시 패배 처리되는 버그가 동반되었다.
- 🟢 해결 방법 (In-Memory Reset):
- OpenLevel 대신, 맵 시작 시 몬스터의 초기 스폰 위치(FMonsterSpawnInfo)를 기억해두고 부활 시 메모리 상에서 즉시 재스폰하여 0ms Lag 스무스 부활을 구현했다.
- J_GameInstance에 bIsGameStarted 플래그를 추가하여 게임 진행 중 부활 시 메인메뉴 UI를 스킵하고 인게임 HUD로 바로 진입하도록 처리했다.
- 플레이어를 체크포인트 위치로 순간이동시키고 HP(100%), 제한시간, 킬 스코어, 탄약 수치를 완전 복원했다.
4. 💥 레벨 전환/재시작 시 BGM 오디오 크래시(Crash) 방지
- 🔴 문제 상황: 다수의 적(2명 이상)과 전투 중 체크포인트로 부활하거나 재시작하면 엔진이 튕기는 현상 발생.
- 🔍 원인 분석: 레벨 재로드 시 몬스터가 순차 소멸하면서 추적 몬스터 수가 0이 되고, 이 순간 BGM을 전투 ➔ 비전투로 전환하려는 시도가 발생했다. 하지만 이미 해체 중인(Tearing Down) 월드의 오디오 컴포넌트 및 타이머 매니저 메모리에 접근(Access Violation)하면서 엔진이 크래시되었다.
- 🟢 해결 코드: 레벨이 파괴 중이거나 레벨 전환으로 인한 소멸일 경우 BGM 조작을 조기 리턴하도록 방어 로직을 추가했다.
// J_GameMode.cpp - 레벨 해체 중일 때 오디오 조작 시 튕김 방지
void AJ_GameMode::UpdateBGMState()
{
// 월드가 없거나 레벨 파괴 중일 때는 BGM 조작 조기 리턴!
if (!GetWorld() || GetWorld()->bIsTearingDown) return;
// ... BGM 전환 로직 ...
}
// MonsterAIController.cpp - 레벨 전환에 의한 소멸 시 BGM 해제 요청 금지
void AMonsterAIController::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
if (EndPlayReason == EEndPlayReason::LevelTransition ||
EndPlayReason == EEndPlayReason::Quit ||
EndPlayReason == EEndPlayReason::EndPlayInEditor)
{
return;
}
// ... 추적 몬스터 수 감소 로직 ...
}
5. 🔫 초기 탄약 UI 불일치 동기화
- 🔴 문제 상황: 시작 시 기본 무기가 칼임에도 UI에 탄약이 1 / 20으로 잘못 표시되다가 무기를 스왑해야 정상화되는 문제.
- 🟢 해결 방법: J_GameMode의 기본 탄약 수량을 5발로 맞추고, CombatComponent::BeginPlay 시점에 현재 장착된 무기가 총(AGunBase*)인지 칼인지 검사하여 시작 시점부터 UI에 Ammo: 0 / 5로 정밀 동기화되도록 수정했다.
💡 오늘의 레슨 (Key Takeaways)
- Enhanced Input 트리거 타입 선택: 매 프레임 연속 입력이 필요한 동작(이동, 시점 회전)은 Triggered, 1회성 동작(점프, 구르기, 공격)은 Started를 명확히 구분하여 사용해야 불필요한 연산과 버그를 막을 수 있다.
- 레벨 전환 시 메모리 안전성: 액터 소멸(EndPlay)이나 레벨 전환 시에는 GetWorld()->bIsTearingDown 및 EEndPlayReason을 반드시 체크해야 한다. 해체 중인 렌더링/오디오/타이머 시스템에 접근하는 것은 치명적인 메모리 크래시를 유발한다.
- OpenLevel vs In-Memory Reset: 씬(Scene) 전환이 필요 없는 단순 캐릭터 부활은 맵을 통째로 다시 읽는 OpenLevel보다 메모리 내에서 상태만 초기화하는 인메모리 리셋 방식이 성능(0ms Lag)과 유저 경험 측면에서 압도적으로 우수하다.
'본캠프 TIL' 카테고리의 다른 글
| 07.30 Unreal 본캠프) 언리얼 엔진 네트워크 기초 (0) | 2026.07.30 |
|---|---|
| 07.27 Unreal 본캠프) 스텔스 AI 인지 시스템, 무기 아키텍처 및 델리게이트 디커플링 (0) | 2026.07.27 |
| 07.22 Unreal 본캠프) 잠입/전투 동적 BGM 페이드 시스템 및 적 투시 아웃라인 구현 (0) | 2026.07.22 |
| 07.20 Unreal 본캠프) C++ 무기 시스템 및 볼륨 저장 시스템 버그 해결 종합 (0) | 2026.07.20 |
| 07.16 Unreal 본캠프) C++를 활용한 아이템 상속 설계와 투사체 무브먼트 연동 (0) | 2026.07.16 |