환경 상호작용의 디버깅
이벤트 입력·조건·결과 실행을 나누어 추적하고 블루프린트 디버거와 충돌 시각화로 상호작용 버그를 해결합니다.
환경 변수와 플레이어 행동을 연결하면 레벨은 훨씬 흥미로워지지만, 문제가 생겼을 때 원인을 찾기도 그만큼 어려워집니다. 환경 상호작용은 여러 블루프린트와 컴포넌트, 충돌과 물리 설정이 함께 움직이므로, 설정 하나만 어긋나도 "문이 안 열린다" 같은 눈에 띄는 버그가 됩니다. 그런데 같은 "안 열린다"라도 입력이 안 들어온 것인지, 조건에서 걸린 것인지, 실행은 했는데 결과가 안 보이는 것인지에 따라 고칠 곳이 전혀 다릅니다.
이 글에서는 버그를 재현하고 원인을 좁혀 가는 기본 흐름을 정리한 뒤, 블루프린트 디버거, Print String, 충돌과 트레이스의 시각화, 콘솔 명령 같은 도구를 살펴봅니다. 그다음 앞에서 만든 상호작용에서 흔히 생기는 버그와 해결책을 모으고, 기록한 값으로 먼저 볼 곳을 좁히는 연습을 합니다. 핵심은 이벤트가 들어왔는지, 조건이 맞는지, 결과가 실행됐는지를 나눠 확인하는 것입니다.
상호작용 버그가 문제인 이유
환경 상호작용은 플레이어 경험에 직접 영향을 주므로, 여기서 생긴 버그는 게임의 품질을 크게 떨어뜨립니다. 문이 열리지 않거나 스위치가 작동하지 않거나 아이템을 주울 수 없으면 플레이어가 더 진행하지 못합니다. 물체가 예상대로 반응하지 않거나 물리가 불안정하면 세계의 사실감이 깨집니다. 같은 버그가 되풀이되면 플레이어는 좌절해 게임을 그만둘 수도 있고, 개발하는 쪽에서도 제때 고치지 못한 버그는 나중에 더 많은 시간을 들여야 합니다.
상호작용 버그는 증상에 따라 먼저 볼 도구가 다릅니다. 그래서 입력, 충돌, 변수, 시간의 흐름을 나눠서 추적해야 원인을 빨리 좁힐 수 있습니다.
원인을 좁혀 가는 흐름
어떤 버그든 디버깅은 원인의 범위를 좁혀 가는 일입니다. 순서는 다음과 같습니다.
먼저 문제를 정확히 재현합니다. 어떤 상황에서 어떤 행동을 했을 때 버그가 생기는지 자세히 기록하고, 늘 생기는지 가끔 생기는지도 확인합니다. 가끔 생기는 버그는 재현하기 어려우므로, 생기는 조건을 최대한 좁혀야 합니다.
다음으로 증상을 분명히 합니다. 문이 열리지 않는 것인지, 발판이 멈추는 것인지, UI가 사라지지 않는 것인지를 정확히 적고, 이 증상이 어떤 컴포넌트나 블루프린트 로직과 관련될지 짐작해 봅니다.
그다음 가능한 원인을 몇 가지 세웁니다. 충돌 설정이 잘못되었을 수도, 입력 이벤트가 생기지 않았을 수도, 타임라인에 문제가 있을 수도, 변수 값이 이상할 수도 있습니다.
마지막으로 세운 원인을 하나씩 시험해 지워 나갑니다. 이때 한 번에 여러 곳을 바꾸지 않도록 주의합니다. 관련 없는 로직이나 변수를 잠시 끄거나 빼서 문제의 범위를 좁히는 것도 도움이 됩니다.
디버깅 도구
블루프린트 디버거
블루프린트 디버거(Blueprint Debugger)는 블루프린트 로직이 실행되는 흐름과 변수 값을 실시간으로 따라가게 해 줍니다. 쓰는 방법은 다음과 같습니다. 디버깅할 BP_Door나 PlayerCharacter 같은 블루프린트를 열고, PIE를 시작한 뒤 블루프린트 에디터의 디버그 대상(Debug Object) 선택에서 실제로 실행 중인 액터 인스턴스를 고릅니다. 같은 블루프린트의 인스턴스가 여러 개라면, 문제가 생긴 그 인스턴스를 골라야 합니다.
그다음 디버깅을 시작할 노드, 예를 들어 OnComponentBeginOverlap이나 상호작용 입력 이벤트에 중단점(breakpoint)을 겁니다. 노드를 선택하고 F9를 누르거나, 오른쪽 클릭 메뉴에서 Add Breakpoint를 고릅니다. 게임을 플레이하다 중단점에 닿으면 실행이 멈추고 블루프린트 에디터로 바뀌며, 디버거 툴바의 Step Over와 Resume으로 실행을 한 단계씩 진행하면서 핀의 값과 Watch에 건 변수를 확인합니다. 단축키는 쓰는 버전의 툴팁이나 에디터 환경설정에서 확인합니다.
이렇게 하면 트리거가 제대로 동작하는지, 입력 이벤트가 생기는지, Branch의 조건이 예상대로 평가되는지, 타임라인이 제대로 재생되는지를 직접 볼 수 있습니다.
Print String
Print String은 게임 화면 왼쪽 위에 글자를 출력해, 변수 값이나 로직이 실행되었는지를 빠르게 확인하게 해 줍니다. 블루프린트 그래프의 원하는 곳에 놓고 출력할 글자나 변수 값을 연결하면 됩니다. "Overlap Started!", "Door Opened!", "Current State: [변수 값]" 같은 메시지로 로직의 흐름을 따라가면, 간단하고 바로 확인할 수 있어 쓸모가 많습니다.
충돌과 트레이스 보기
충돌 형상은 레벨 에디터 뷰포트의 Show 메뉴에서 Collision을 켜서 봅니다. 메시 에디터에서는 Simple Collision과 Complex Collision 표시도 함께 확인합니다. 이것으로 트리거 볼륨의 크기와 위치가 맞는지, 물체의 충돌 형상이 예상대로인지 확인할 수 있으며, 표시된 색만으로 충돌의 종류를 단정하지는 않습니다.
라인 트레이스는 Line Trace By Channel 노드의 Draw Debug Type을 For Duration이나 Persistent로 정하면, 쏜 선과 맞은 지점이 화면에 그려집니다. 시선으로 상호작용하는 범위가 맞는지, 트레이스가 기대한 물체에 맞는지 확인하는 데 씁니다.
콘솔 명령과 로그
show collision은 지원하는 개발용 빌드의 게임 콘솔에서 충돌 표시를 켜고 끕니다. 콘솔은 보통 백틱 키로 열지만, 프로젝트의 Console Keys 설정을 확인합니다. stat physics는 그 버전과 빌드가 제공하는 물리 통계를 보는 보조 수단이며, 자세한 CPU 시간은 언리얼 인사이트로, 카오스의 상태는 카오스 비주얼 디버거의 기록으로 따로 봅니다.
로그로 추적하고 싶다면 Print String에서 Print to Log를 켜고 Print to Screen을 끄면, 출력 로그(Output Log)를 중심으로 볼 수 있습니다. 다만 이 노드는 개발용으로만 동작하므로, 배포용(Shipping) 빌드에서 쓸 영구적인 로그 수단으로 생각하지 않습니다.
흔한 버그와 해결책
상호작용이 전혀 동작하지 않을 때
가장 먼저 의심할 것은 겹침 이벤트입니다. 트리거의 Generate Overlap Events가 꺼져 있다면, 트리거와 상대 컴포넌트 양쪽에서 이 설정을 켜고, 충돌 검사와 채널 응답도 확인합니다. 충돌 프리셋이 NoCollision이거나 Overlap이 아니라면, OverlapOnlyPawn이나 OverlapAll을 쓰거나, Custom에서 Pawn에 Overlap으로 응답하게 정합니다.
다음은 캐스트입니다. 잘못된 액터 타입으로 캐스트해 실패하고 있을 수 있으므로, Cast To 노드의 대상 클래스를 정확히 확인하고, Cast Failed 핀에 Print String을 연결해 실패하는지 봅니다.
입력도 확인합니다. 기존 방식의 액션 매핑이나, 향상된 입력의 입력 액션과 매핑 컨텍스트가 제대로 등록되었는지 봅니다. 그리고 입력을 받는 플레이어 캐릭터나 컨트롤러와, 거기서 대상 액터로 가는 호출을 따로따로 추적합니다.
UI가 나타나지 않거나 사라지지 않을 때
위젯이 Add to Viewport되지 않았거나 Remove from Parent되지 않았을 수 있습니다. 위젯 참조가 유효한지와 화면에 추가되어 있는지를 따로 확인하고, 캐스트에 실패하거나 대상이 무효해지는 경로에서도 기존 안내를 숨기는지 봅니다. Set Visibility가 호출되지 않았거나 엉뚱한 조건에서 호출되고 있다면, Branch의 조건과 Set Visibility의 호출을 디버거로 따라갑니다.
물체가 부드럽게 움직이지 않거나 멈출 때
타임라인이 Play나 Reverse되지 않았거나, Update 핀이 연결되지 않았을 수 있으므로 타임라인의 연결과 재생 상태를 확인합니다. Lerp의 Alpha가 제대로 보간되지 않거나 A와 B 핀의 값이 잘못되었다면, Print String으로 Alpha와 출력값을 찍어 봅니다. 물리로 움직이는 물체가 갑자기 멈추거나 떨린다면, Linear Damping과 Angular Damping, 잠들기의 기준인 Sleep Threshold 같은 물리 설정을 조정하고, 물리 통계를 확인합니다.
상호작용의 순서가 꼬이거나 상태가 이상할 때
Branch, FlipFlop, DoOnce 같은 흐름 제어 노드를 잘못 썼을 수 있습니다. 블루프린트 디버거로 로직을 한 단계씩 따라가며, bIsOpen이나 bIsMoving 같은 상태 변수가 올바르게 바뀌는지 확인합니다.
기록한 값으로 먼저 볼 곳 좁히기
디버깅은 되풀이해서 연습할수록 익숙해집니다. "문이 열려야 하는데 닫혀 있다"나 "스위치를 눌러도 반응이 없다"처럼 구체적인 문제 상황을 정하고, "오버랩 이벤트가 생기지 않는 것 같다"처럼 가설을 세운 뒤, Print String과 시각화, 블루프린트 디버거로 그 가설을 확인합니다. 한 번에 한 문제에 집중하고, 고친 뒤에는 다른 문제가 새로 생기지 않았는지 다시 시험합니다.
연습할 때는 재현한 조건, 확인한 값, 바꾼 항목, 같은 조건에서 다시 시험한 결과를 기록해 둡니다. 그리고 확인하기 전에 예상한 원인과 실제로 확인한 원인을 구분해 적습니다. 예상이 맞았는지를 나중에 돌아볼 수 있어야 다음 디버깅이 빨라집니다.
예를 들어 문이 첫 요청에는 열리는데 두 번째 요청부터 반응하지 않는다고 해 봅시다. 입력 로그가 두 번 찍히고 CandidateDoor도 같은 유효한 문을 가리킨다면, 입력 매핑부터 다시 만들 필요는 없습니다. 입력은 문까지 제대로 오고 있기 때문입니다. 대신 요청할 때의 bIsAnimating과, 타임라인이 Finished에 닿았는지를 확인합니다. 만약 Update의 로그와 문의 회전은 있는데 Finished의 로그가 없고 bIsAnimating이 True로 남아 있다면, 완료 핀과 입력 잠금을 푸는 경로부터 봐야 합니다. 이 로그만으로 엔진의 물리 오류라고 확정하지는 않습니다. 완료 경로를 고친 뒤에는 같은 문으로 열기, 완료, 닫기, 완료와, 움직이는 중의 재입력을 다시 시험합니다.
정리
- 상호작용 버그는 이벤트가 들어왔는지, 조건이 맞는지, 결과가 실행됐는지를 나눠 확인해야 원인을 빨리 좁힐 수 있습니다.
- 디버깅은 재현, 증상 확인, 원인 가설, 하나씩 시험하는 순서로 하며, 한 번에 여러 곳을 바꾸지 않습니다.
- 블루프린트 디버거는 디버그 대상 인스턴스를 고르고 중단점(F9)을 걸어 흐름과 값을 따라가고,
Print String은 빠른 확인에 쓰지만 개발용 빌드에서만 동작합니다. - 충돌은 Show 메뉴의 Collision이나
show collision으로, 트레이스는Draw Debug Type으로 보며, 표시 색만으로 종류를 단정하지 않습니다. - 겹침 설정, 캐스트, 입력 등록, 위젯의 화면 추가, 타임라인의 연결, 상태 변수의 변화가 흔한 원인입니다.
- 재현 조건과 확인한 값, 바꾼 항목, 재시험 결과를 기록하고, 예상한 원인과 실제로 확인한 원인을 구분합니다.
연습 문제
1. 벽을 봐도 남는 안내
문을 보다가 벽으로 시선을 돌렸더니, 트레이스의 Return Value는 True, 상호작용 타입으로의 캐스트는 Cast Failed, PrevHitActor는 이전 문 그대로이고 안내도 남아 있습니다. 트레이스의 길이를 늘리는 것부터 해야 할까요?
해설 보기
아닙니다. 트레이스는 벽에 맞았으므로 True가 나오는 것이 정상이고, 길이는 문제가 아닙니다. 기록한 값은 캐스트에 실패했는데도 후보와 강조, 안내를 정리하지 않았다는 것을 보여 줍니다.
그래서 캐스트에 실패한 경로에서 정리 함수를 부르는지부터 확인합니다. 이것은 True와 False 두 갈래만 보고, True면 늘 문이라고 가정한 오류입니다. 고친 뒤에는 같은 벽, 빈 공간, 다른 문으로 시선을 옮겨 가며, 각 실패와 전환 경로에서 정리가 되는지 다시 확인합니다.
2. 디버거가 멈추지 않는다
BP_Door의 ToggleDoor에 중단점을 걸고 플레이했는데, 문 앞에서 키를 눌러도 디버거가 멈추지 않습니다. 레벨에는 BP_Door가 세 개 있습니다. 무엇을 확인해야 할까요?
해설 보기
먼저 블루프린트 에디터의 디버그 대상이 지금 상호작용하는 그 문 인스턴스로 골라져 있는지 확인합니다. 다른 인스턴스를 고르고 있었다면, 이 문에서 실행되어도 멈추지 않을 수 있습니다.
인스턴스가 맞는데도 멈추지 않는다면, ToggleDoor가 실제로 호출되지 않는다는 뜻입니다. 그때는 한 단계 앞인 플레이어의 입력 이벤트에 중단점이나 Print String을 두어, 입력이 들어오는지, CandidateDoor가 유효한지, 호출이 그 문을 Target으로 하는지를 차례로 확인합니다.
3. 배포 빌드에서 로그가 없다
개발 중에 Print String으로 상호작용의 흐름을 기록해 두었는데, 배포용 빌드로 시험하니 화면에도 로그에도 아무것도 나오지 않습니다. 기능이 고장 난 걸까요?
해설 보기
꼭 그렇지는 않습니다. Print String은 개발용으로만 동작하는 노드라서, 배포용(Shipping) 빌드에서는 출력되지 않습니다. 출력이 없다는 사실만으로 기능이 실행되지 않았다고 판단할 수 없습니다.
배포 빌드에서 확인할 로그가 필요하다면, 배포 빌드에서도 남는 별도의 로그 방식을 쓰거나, 개발용 빌드에서 같은 조건으로 재현해 확인합니다.
블루프린트 디버거의 사용법은 Epic의 블루프린트 디버깅 예제에서 확인할 수 있습니다.
이것으로 8장을 마칩니다. 8장에서는 트리거와 상태 변수, 타임라인으로 문을 여는 상호작용을 만들고, 시선으로 대상을 찾아 안내를 띄우고 정리하는 방법, 남은 거리의 비율로 움직이는 발판, 광맥이 자원의 양을 바꾸고 성공한 요청만 인벤토리에 반영하는 구조, 그리고 이벤트와 조건, 결과를 나눠 상호작용 버그를 찾는 방법까지 환경 상호작용을 구현했습니다. 다음 장의 환경 요소의 성능 분석부터는 지금까지 만든 환경의 비용을 측정하는 데서 시작해, 에셋과 레벨의 최적화, 환경 설정이 성능에 주는 영향, 메모리와 CPU 사용량의 최적화까지 성능을 본격적으로 다룹니다.