Unreal 게임 개발 과정 TIL

Today's I Learning

본캠프 TIL

07.20 Unreal 본캠프) C++ 무기 시스템 및 볼륨 저장 시스템 버그 해결 종합

Sunlight__ 2026. 7. 20. 21:26

1. 오디오 볼륨 설정 영구 저장 및 UMG 연동 시스템

1.1 세이브 시스템 구현 및 사운드 클래스 믹스 연동 (SaveGame System)

  • 구현 내용: 게임을 껐다 켜도 오디오 설정이 보존되도록 USaveGame을 상속받는 UJ_SaveGame 클래스를 작성하여 마스터, 배경음(BGM), 효과음(SFX) 볼륨 수치를 로컬 파일에 영구 기록하도록 했다.
  • 동적 적용: UJ_GameInstance에서 세이브 슬롯 로드 및 저장을 제어하고, UGameplayStatics::SetSoundMixClassOverride를 사용해 게임 내 실제 사운드 클래스(Sound Class) 볼륨을 동적으로 오버라이드하도록 바인딩했다.

1.2 UMG 슬라이더 초기화 레이스 컨디션 및 값 덮어쓰기 해결

  • 문제 상황: 게임 설정 위젯(WBP_Settings) 생성 시 슬라이더에 세이브된 볼륨값을 대입(SetValue)하는 과정에서, 슬라이더의 OnValueChanged 이벤트가 델리게이트로 연쇄 호출되어 세이브된 값이 0.0으로 다시 덮어씌워져 리셋되는 레이스 컨디션이 발생했다. 덧붙여 위젯의 텍스트 상자(Text Box) 값과 슬라이더(Slider) 핀 연결이 잘못되어 껐다 켤 때 수치가 저장되지 않는 UMG 노드 배선 버그도 있었다.
  • 해결 방법: 위젯 블루프린트에 bIsInitializing이라는 불리언 플래그를 추가했다. Event Construct 시작 시 플래그를 true로 설정하여 슬라이더 값 변경 이벤트가 실행되어도 세이브 로직을 타지 않게 예외 처리하고, 세팅값 대입이 완전히 종료된 후에 플래그를 false로 돌려 UI 초기화 과정에서의 무한 루프 및 데이터 덮어쓰기 버그를 원천 차단했다.

1.3 PIE 오디오 디바이스 타이밍 이슈 해결

  • 문제 상황: 오디오 설정을 로드하고 적용하는 코드를 최초에는 UGameInstance::Init()에 배치했으나, 에디터 재생(PIE) 환경에서는 오디오 디바이스가 GameInstance::Init()보다 미세하게 늦게 활성화되기 때문에 볼륨 설정이 적용되지 않고 무시되어 항상 볼륨 100%의 기본 소리로 시작되었다.
  • 해결 방법: 게임 월드가 완전히 생성되고 액터들이 배치된 직후인 AJ_GameMode::BeginPlay() 단계로 세이브 로드 및 볼륨 믹서 적용 함수(LoadAndApplyVolumeSettings()) 호출 시점을 변경하여 오디오 디바이스 활성화 타이밍 불일치 문제를 해결했다.

2. 무기 전투 시스템 버그 해결 및 최적화

2.1 무기 스왑 무한 탄약 꼼수 차단 (Weapon Swap Infinite Ammo Exploit)

  • 문제 상황: 무기 교체(SwitchWeapon) 시 기존 무기 액터를 파괴하고 새로 스폰하는 구조에서, 새 총 액터가 스폰될 때 생성자 기본값에 의해 약실 장전 상태(bIsLoaded = true)로 강제 리셋되어 무기 스왑만 반복하면 무한 사격이 가능했던 밸런스 붕괴 현상이 있었다.
  • 해결 방법: 캐릭터의 영구적인 컴포넌트인 CombatComponent 내부에 총의 마지막 장전 상태를 기록해 둘 bGunIsLoaded 변수를 마련했다. 총이 파괴되기 직전에 장전 상태(IsLoaded())를 백업하고, 새 총이 스폰되는 즉시 커스텀 Setter 함수(SetLoaded())를 통해 이전 상태를 이식하도록 구현하여 스왑 꼼수를 차단했다.

2.2 장전 중 무기 스왑 시 사운드 겹침 및 잔존 버그

  • 문제 상황: 총을 재장전하는 도중에 무기를 교체하면 장전 동작은 취소되지만, 장전 효과음은 계속 공중에 남아서 들리고 스왑을 반복할 때마다 장전 소리가 여러 개 겹쳐 들렸다. 기존 사운드 재생이 일회성 방치 방식인 UGameplayStatics::PlaySoundAtLocation으로 구현되어 있어, 총 액터가 파괴되어도 사운드가 끝까지 재생되었기 때문이다.
  • 해결 방법: 사운드 재생 방식을 액터에 부속되는 UGameplayStatics::SpawnSoundAttached로 수정하여 반환된 UAudioComponent* 포인터를 보관했다. 총의 수명 주기 해제 함수인 EndPlay를 오버라이드하여, 액터가 파괴될 때 오디오 컴포넌트가 재생 중이라면 즉시 재생을 중단(Stop())시키도록 예외 처리했다.

2.3 활 스킬(멀티샷) 화살 증발 및 공중 멈춤 버그

  • 문제 상황: 활 스킬 사용 시 부채꼴 모양으로 3발이 나가야 하나, 오른쪽 1발만 발사되거나(콜리전 겹침 차단), 3발 모두 캐릭터 앞 공중에 마네킹처럼 굳어서 멈춰 서는 현상이 발생했다.
  • 원인 및 해결 과정:
    • 스폰 콜리전 체크 실패: 동일 프레임/동일 좌표에서 동시에 3발을 생성하려다 보니 엔진이 충돌(Overlap)로 인식해 생성을 차단했다. ➔ 스폰 파라미터에 AlwaysSpawn 옵션을 강제 지정하여 해결했다.
    • 화살 충돌 자해 판정: 3발이 생성된 직후 서로 겹쳐 충돌하면서 각 화살의 OnHit가 발동해 날아가기도 전에 공중분해 되었다. ➔ OnHit 초입에 동일 화살 계열(AJ_ProjectileBase) 간의 충돌은 무시하고 관통하도록 예외 처리했다.
    • 속도 미주입 및 0 곱하기 수학 에러: 일반 사격과 달리 스킬 사격에 화살 속도(SetSpeed) 설정이 누락되어 속도가 0이었다. 속도가 0인 상태에서 SetSpeed를 부르면 방향 벡터인 (0,0,0).GetSafeNormal() * Speed가 수행되어 속도가 계속 0으로 남는 수학적 예외가 있었다. ➔ SetSpeed 내부에 속도 방향이 0일 경우 화살의 전방 벡터(GetActorForwardVector())를 대입하는 안전장치를 추가했다.
    • 무기/캐릭터 상호 물리 충돌: 화살이 생성되는 궤적이 캐릭터의 팔이나 손에 쥔 활에 스쳐 지나가며 물리적 속도가 0으로 꺾였다. ➔ 화살의 BeginPlay에서 쏜 주인 캐릭터(MyPawn) 및 주인이 장착 중인 모든 부착 액터(활 무기)와 상호 이동 무시(IgnoreActorWhenMoving, MoveIgnoreActorAdd) 설정을 양방향으로 걸어주어 깔끔하게 날아가도록 해결했다.

💡 주요 배움 (Key Takeaways)

  • 오디오 믹싱 타이밍: 언리얼 에디터(PIE)에서는 오디오 시스템 초기화 시점이 GameInstance::Init()보다 늦기 때문에, 오디오 설정을 로드하고 적용하는 작업은 디바이스가 보장되는 GameMode::BeginPlay() 이후 시점을 타겟으로 잡아야 안전하다.
  • UMG 슬라이더의 델리게이트 함정: 코드/블루프린트로 슬라이더의 SetValue를 호출하는 것은 유저의 물리적인 조작과 동일하게 OnValueChanged 이벤트를 즉각 트리거하므로, UI 초기화 시에는 불리언 락(Lock) 플래그를 통한 상호 간섭 예방 장치가 필수적이다.
  • 액터 파괴와 변수 수명: 무기를 장착/해제할 때 액터를 스폰/파괴하는 방식이라면, 보존해야 할 상태 데이터는 수명이 보장되는 캐릭터 컴포넌트(CombatComponent)나 게임 인스턴스에 위임하여 보관해야 한다.