본문 바로가기

블루프린트 디버깅 기초

Print String과 중단점, 디버그 오브젝트를 활용해 실행 흐름과 변수 값을 추적하는 기본 절차를 익힙니다.

그래프를 만들다 보면 컴파일은 성공했는데 결과가 예상과 다른 순간이 반드시 옵니다. 메시지가 아예 나오지 않을 수도 있고, 나오기는 하는데 값이 틀릴 수도 있습니다. 이렇게 로직이 의도대로 동작하지 않을 때 무엇이 문제인지 찾아 고치는 과정을 디버깅(debugging)이라고 합니다. 블루프린트도 텍스트 코드처럼 짐작으로 고치기 시작하면 시간만 쓰게 되므로, 실행이 어디까지 갔고 그때 값이 무엇이었는지를 도구로 확인하는 습관이 필요합니다.

이 글에서는 언리얼 엔진이 블루프린트에 제공하는 기본 디버깅 도구 세 가지를 익힙니다. 원하는 지점에서 값을 출력하는 Print String, 실행을 특정 노드에서 멈추는 브레이크포인트(breakpoint), 그리고 같은 블루프린트의 여러 인스턴스 가운데 하나를 골라 보는 디버그 오브젝트(Debug Object) 선택입니다. 공식 한국어 문서에서는 브레이크포인트를 중단점이라고 부르며, 두 이름은 같은 기능을 가리킵니다.


가장 간단한 디버깅 방법은 앞 글에서 쓴 Print String을 확인하려는 지점에 끼워 넣는 것입니다. 확인하려는 실행 경로에 Print String을 놓고 실행 핀을 이으면, 실행이 그 지점에 도달했는지가 화면 메시지로 드러납니다. 변수 값을 보고 싶다면 그 변수를 In String에 연결합니다. 정수나 실수처럼 문자열이 아닌 값은 핀을 끌어다 놓을 때 에디터가 문자열 변환 노드를 끼워 넣습니다.

이 방법은 두 가지 질문에 답할 때 특히 쓸모가 있습니다. 하나는 "값이 지금 얼마인가"로, 예를 들어 "현재 체력: "과 CurrentHealth 변수를 이어 붙여 출력하면 체력이 바뀌는 과정을 플레이하며 볼 수 있습니다. 다른 하나는 "어느 경로로 갔는가"로, Branch 노드의 True와 False 출력에 서로 다른 문구의 Print String을 이어 두면 어떤 조건이 만족되었는지 알 수 있습니다. 메시지가 빨리 사라진다면 Duration 입력을 늘립니다. 기본값은 2초입니다.

Print String은 요리 중간에 간을 보는 것과 비슷합니다. 로직의 특정 지점을 잠깐 맛보는 방식이라 간단하면서도 원인을 좁혀 나가는 데 효과적입니다. 다만 출력을 너무 많이 넣으면 화면이 메시지로 덮여 오히려 읽기 어려우므로, 확인이 끝난 임시 출력은 지웁니다.


브레이크포인트로 실행 멈추기

Print String이 특정 지점의 값을 출력해 보여 준다면, 브레이크포인트는 실행을 특정 노드에서 멈추고 그 순간의 흐름과 값을 직접 들여다보게 해 줍니다. 브레이크포인트를 걸려면 블루프린트 에디터의 그래프에서 멈추고 싶은 노드를 마우스 오른쪽 버튼으로 클릭하고 Add Breakpoint를 선택합니다. 그러면 노드 왼쪽 위에 빨간색으로 채워진 팔각형이 나타납니다. 브레이크포인트를 끄면 테두리만 남은 팔각형으로, 걸 수 없는 위치라면 느낌표가 있는 노란 아이콘으로 표시됩니다.

이제 Play로 에디터에서 플레이를 시작합니다. 실행이 브레이크포인트가 걸린 노드에 도달하면, 그 노드가 실행되기 직전에 게임이 일시 정지되고 블루프린트 에디터가 앞으로 나와 해당 노드를 큰 빨간 화살표로 강조합니다. 이 상태에서 데이터 핀 위에 마우스를 올리면 그 핀의 현재 값을 볼 수 있습니다. 자주 확인할 핀이라면 핀 이름을 오른쪽 클릭해 Watch this value를 선택해 두면 됩니다.

Watch로 보는 값은 그 노드가 마지막으로 실행되었을 때의 값입니다. 그래서 아직 한 번도 실행되지 않은 노드의 핀에는 값이 나타나지 않으며, 값이 표시되지 않는다고 그 값이 0이거나 비어 있다는 뜻은 아닙니다. 지금 보고 있는 값이 어느 인스턴스의 것이고 언제 평가된 것인지를 함께 확인해야 하는 이유입니다.

일시 정지된 동안에는 툴바의 실행 제어 버튼으로 다음 동작을 정합니다. Resume은 다음 브레이크포인트에 도달하거나 플레이를 끝낼 때까지 실행을 이어 갑니다. Step Over는 현재 노드를 실행하고 다음 노드에서 다시 멈추므로, 노드를 하나씩 따라가며 흐름을 세밀하게 추적할 수 있습니다. 현재 노드가 함수 호출이라면 Step Into로 그 함수 안으로 들어가 한 노드씩 볼 수 있습니다. 어떤 블루프린트의 어떤 함수가 실행 중인지 더 넓게 보고 싶다면 Tools 메뉴나 블루프린트 에디터의 Debug 메뉴에서 블루프린트 디버거(Blueprint Debugger)를 엽니다. 이 창은 브레이크포인트와 Watch 목록, 지금 실행 중인 함수가 맨 위에 오는 호출 스택(Call Stack)을 보여 줍니다.

브레이크포인트는 정밀 검사를 위해 컨베이어 벨트를 잠시 멈추는 것과 같습니다. 멈춘 지점에서 확인한 값과 호출 순서를 예상과 비교하면 원인이 어디쯤인지 좁힐 수 있습니다.


디버그 오브젝트로 인스턴스 고르기

같은 블루프린트로 만든 인스턴스가 레벨에 여럿 있으면, 디버깅 정보가 어느 인스턴스의 것인지 헷갈리기 쉽습니다. 특정 인스턴스에서만 문제가 생긴다면 더욱 그렇습니다. 이때 쓰는 것이 블루프린트 에디터 툴바의 디버그 오브젝트(Debug Object) 드롭다운입니다.

플레이하는 동안 블루프린트 에디터를 열어 둔 채, 툴바의 디버그 오브젝트 드롭다운을 누르면 현재 월드에 있는 이 블루프린트의 인스턴스 목록이 나타납니다. 여기서 살펴볼 인스턴스를 고르면, 이후 그래프에 표시되는 실행 흐름의 강조와 핀의 값은 그 인스턴스의 것만 나타납니다. 어느 인스턴스인지 이름만으로 구분하기 어렵다면 레벨 뷰포트나 아웃라이너(Outliner)에서 그 액터를 선택해 이름을 확인한 뒤 드롭다운에서 같은 이름을 고릅니다.

이렇게 대상을 하나로 좁히면 여러 액터가 섞인 복잡한 장면에서도 불필요한 정보를 걸러 내고, 문제가 생긴 인스턴스의 흐름에만 집중할 수 있습니다.


문제를 다시 만들고 확인하는 순서

도구보다 먼저 정해야 할 것은 문제를 다시 일으키는 조건입니다. 어느 맵에서, 어느 액터 인스턴스로, 어떤 입력을 어떤 순서로 넣었고, 시작할 때 값이 무엇이었는지를 짧게 적어 둡니다. 그래야 고친 뒤에 같은 조건으로 다시 플레이해서 정말 고쳐졌는지 확인할 수 있습니다.

조건을 정했다면 이 글의 도구를 다음 순서로 씁니다. 먼저 디버그 오브젝트로 문제가 생긴 인스턴스를 고릅니다. 다음으로 그 인스턴스의 실행이 조사하려는 노드에 도달하는지를 브레이크포인트나 임시 Print String으로 봅니다. 도달한다면 그 시점에 핀의 값이 평가되었는지 Watch로 확인하고, 그다음에 값을 대입하는 경로를 조사합니다. 확인이 끝나면 임시로 넣은 Print String과 브레이크포인트는 정리해서 다음 작업에 섞이지 않게 합니다.


정리

  • Print String을 확인하려는 실행 경로에 끼워 넣으면 실행이 그 지점에 도달했는지와 그때의 값을 화면에서 볼 수 있습니다.
  • 브레이크포인트는 노드를 오른쪽 클릭해 Add Breakpoint로 걸며, 실행이 그 노드에 도달하면 노드가 실행되기 직전에 멈춥니다.
  • 멈춘 동안 핀 위에 마우스를 올리거나 Watch this value로 값을 보고, Resume, Step Over, Step Into로 실행을 이어 갑니다.
  • Watch의 값은 노드가 마지막으로 실행되었을 때의 값이며, 실행되지 않은 노드에는 값이 나타나지 않습니다.
  • 인스턴스가 여럿이면 디버그 오브젝트 드롭다운으로 살펴볼 인스턴스를 하나 골라야 합니다.

연습 문제

1. 누구의 값을 언제 봤는가

점수 변수를 가진 액터 A와 B가 같은 월드에 있고, 실행 중 확인하려는 대상은 B입니다. B의 출력 노드에 들어갈 값은 20이어야 하는데 Watch에는 10이 보입니다. 그런데 디버그 오브젝트는 A로 선택되어 있고, B의 해당 경로가 아직 실행되었는지도 확인하지 않았습니다. 이것만으로 B에서 점수를 대입하는 부분이 잘못되었다고 결론 내릴 수 있을까요?

해설 보기

결론 내릴 수 없습니다. 지금 Watch에 보이는 10은 A의 값일 수 있기 때문입니다. 먼저 디버그 오브젝트를 실제로 플레이 중인 B로 바꿉니다.

다음으로 B의 실행 선이 조사하려는 노드에 도달하는지를 브레이크포인트나 임시 출력으로 봅니다. 그 시점에 값이 평가되었는지 확인한 뒤에야 대입 경로를 조사합니다. 다른 인스턴스의 마지막 값과 현재 B의 값을 같은 것으로 묶어서는 안 되며, 값이 표시되지 않는다는 사실도 곧바로 0이나 None이라는 뜻은 아닙니다.

2. 어느 경로로 갔는지 알아내기

체력이 0 이하인지 확인하는 Branch 노드 뒤에서 캐릭터가 쓰러지는 처리가 실행되지 않습니다. 브레이크포인트를 쓰지 않고, 체력이 실제로 얼마였고 Branch가 어느 출력으로 갔는지 한 번의 플레이로 알아내려면 어떻게 할까요?

해설 보기

Branch 바로 앞에 체력 변수를 출력하는 Print String을 두고, Branch의 True와 False 출력에도 각각 "True 경로", "False 경로"처럼 서로 다른 문구의 Print String을 이어 둡니다.

한 번 플레이하면 체력 값과 함께 어느 경로 문구가 나왔는지가 보입니다. 아무 문구도 나오지 않는다면 Branch까지 실행이 도달하지 않은 것이므로, 그 앞의 이벤트와 실행 선을 확인합니다. 확인이 끝나면 임시 출력은 지웁니다.

3. 브레이크포인트에서 멈추지 않는다

BP_WelcomeMessage의 Print String 노드에 브레이크포인트를 걸고 Play했는데, 메시지는 화면에 나오는데도 실행이 멈추지 않았습니다. 브레이크포인트 아이콘은 테두리만 있는 팔각형입니다. 무엇을 확인해야 할까요?

해설 보기

테두리만 있는 팔각형은 꺼진 브레이크포인트입니다. 메시지가 나왔으므로 실행은 그 노드까지 도달했지만, 브레이크포인트가 꺼져 있어서 멈추지 않은 것입니다.

노드를 오른쪽 클릭해 브레이크포인트를 다시 켜고, 아이콘이 빨간색으로 채워진 팔각형이 되었는지 확인한 뒤 다시 플레이합니다. 노란 느낌표 아이콘이라면 그 위치에는 브레이크포인트를 걸 수 없다는 뜻이므로 다른 노드를 고릅니다.

브레이크포인트와 Watch, 블루프린트 디버거 창의 구성은 언리얼 엔진 공식 문서의 블루프린트 디버거 설명에서 확인할 수 있습니다.

이것으로 1장을 마칩니다. 1장에서는 블루프린트가 무엇이고 C++과 어떻게 일을 나누는지에서 시작해, 노드와 핀, 실행 선과 데이터 선을 읽는 법, 블루프린트 에디터의 패널 구성, 첫 Actor 블루프린트를 만들어 BeginPlay에서 메시지를 출력하는 과정, 그리고 Print String과 브레이크포인트, 디버그 오브젝트로 실행과 값을 확인하는 방법까지 익혔습니다. 다음 장의 변수 유형과 기본 활용법부터는 변수와 함수, 이벤트, 흐름 제어 노드, 매크로처럼 블루프린트 그래프를 이루는 기본 요소를 하나씩 다룹니다.