본문 바로가기

첫 번째 블루프린트 생성 및 실행

액터 블루프린트를 만들고 BeginPlay와 Print String을 연결해 컴파일·배치·실행까지 직접 확인합니다.

블루프린트 에디터의 패널을 알았다면, 이제 직접 하나를 만들어 실행해 볼 차례입니다. 처음 만드는 블루프린트는 기능이 작을수록 좋습니다. 결과가 예상과 다를 때 확인할 곳이 적기 때문입니다.

이 글에서 만들 것은 게임이 시작될 때 화면에 환영 메시지를 한 줄 띄우는 액터 블루프린트입니다. 에셋을 만들고, 이벤트 그래프에 BeginPlay와 Print String을 잇고, 컴파일과 저장을 한 뒤, 레벨에 배치해 플레이하는 순서를 따라갑니다. 이 순서는 앞으로 만들 거의 모든 블루프린트에서 되풀이되는 기본 작업 흐름입니다.


Actor 블루프린트 만들기

블루프린트 에셋은 콘텐츠 브라우저(Content Browser)에서 만듭니다. 콘텐츠 브라우저는 프로젝트의 모든 에셋(asset)을 폴더 구조로 관리하는 패널입니다. 에셋을 둘 폴더를 연 뒤 빈 공간을 마우스 오른쪽 버튼으로 클릭하면 컨텍스트 메뉴가 열리고, 여기서 Blueprint Class를 고르면 됩니다. 콘텐츠 브라우저 위쪽의 Add 버튼을 눌러도 같은 메뉴가 나옵니다.

그러면 부모 클래스를 고르는 Pick Parent Class 창이 나타납니다. 자주 쓰는 클래스 목록에서 Actor를 선택합니다. Actor는 게임 월드에 배치할 수 있는 모든 오브젝트의 기본 클래스이므로, 레벨에 놓고 플레이할 이번 예제에 알맞습니다. 새 에셋이 만들어지면 이름을 BP_WelcomeMessage로 입력하고 Enter 키를 누릅니다. 이제 콘텐츠 브라우저에 BP_WelcomeMessage 블루프린트 에셋이 보입니다.


BeginPlay 이벤트 준비하기

BP_WelcomeMessage를 두 번 클릭해 블루프린트 에디터를 열면, 앞 글에서 본 패널이 나타납니다. 게임이 시작될 때 메시지를 출력하려면 먼저 실행을 시작할 이벤트 노드가 필요합니다. 이벤트 그래프(Event Graph) 탭으로 이동해 보면, 새로 만든 Actor 블루프린트에는 Event BeginPlay, Event ActorBeginOverlap, Event Tick 노드가 흐리게 미리 놓여 있습니다. 흐린 노드는 아직 연결된 노드가 없다는 표시일 뿐이므로, 여기에 선을 이으면 그대로 쓸 수 있습니다.

Event BeginPlay가 보이지 않는다면 이벤트 그래프의 빈 곳을 마우스 오른쪽 버튼으로 클릭해 컨텍스트 메뉴를 열고, 검색창에 BeginPlay를 입력해 Event BeginPlay를 추가합니다. 이미 있는데 하나 더 추가하려 하면 에디터가 기존 노드로 안내하므로, 같은 이벤트 노드는 한 그래프에 하나만 둡니다.

Event BeginPlay는 액터 인스턴스가 플레이를 시작할 때 받는 이벤트입니다. 레벨에 미리 배치한 액터뿐 아니라 게임 도중에 생성한 액터도 그 액터가 플레이를 시작하는 시점에 이 이벤트를 받습니다. 실행되는 것은 레벨에 있는 인스턴스이며, 콘텐츠 브라우저의 에셋 파일 자체가 실행되는 것은 아닙니다.


이제 Event BeginPlay가 실행될 때 메시지를 출력하도록 이어 봅니다. 메시지 출력에는 Print String 노드를 씁니다. Event BeginPlay 노드 오른쪽의 실행 핀을 마우스로 누른 채 빈 곳까지 끌어다 놓으면 노드 검색창이 나타나는데, 여기에 Print String을 입력해 노드를 선택합니다. 이렇게 핀에서 끌어 만든 노드는 실행 핀이 자동으로 연결되어, 두 노드 사이에 흰색 실행 선이 생깁니다.

Print String은 개발 중에 문자열을 화면과 로그에 출력해 실행 흐름을 확인할 때 쓰는 노드입니다. 노드의 In String 입력 칸에는 기본으로 Hello가 적혀 있습니다. 이 칸을 클릭해 내용을 나 혼자 언리얼 블루프린트 시작!으로 바꿉니다. 이제 게임이 시작되면 BeginPlay의 실행 선을 따라 Print String이 실행되고, In String의 문구가 출력됩니다.

Print String 노드 아래쪽의 화살표를 펼치면 다른 입력도 보입니다. Print to Screen과 Print to Log는 각각 화면과 출력 로그(Output Log)에 문구를 남길지를 정하며, 기본으로 둘 다 켜져 있습니다. Duration은 화면에 문구를 표시할 시간(초)이고 기본값은 2입니다. Key에 이름을 넣으면 같은 키의 이전 화면 메시지를 새 메시지로 바꿔 씁니다. 이 노드는 개발용이라서, 배포용인 Shipping 빌드에서는 아무것도 출력하지 않습니다.


컴파일하고 저장하기

블루프린트를 고친 내용은 컴파일(compile)해야 실행에 반영됩니다. 컴파일은 작성한 그래프를 엔진이 실행할 수 있는 형태로 바꾸는 과정으로, 이때 연결이 잘못된 노드가 있으면 오류나 경고가 표시됩니다. 블루프린트 에디터 왼쪽 위 툴바의 Compile 버튼을 누르고, 성공해 버튼에 초록색 체크 표시가 나타나는지 확인합니다.

컴파일이 끝나면 옆의 Save 버튼으로 에셋을 저장합니다. 저장은 에셋을 디스크에 보존하는 단계일 뿐, 액터 인스턴스를 만들거나 플레이를 시작하는 동작이 아닙니다. 그래서 저장까지 마쳐도 아직 화면에는 아무 일도 일어나지 않습니다.

더 깊이: Compile 버튼을 누르면 엔진 안에서 일어나는 일

블루프린트 에셋은 그대로 실행되는 것이 아니라, 컴파일할 때마다 엔진이 쓸 클래스(class)로 바뀝니다. 블루프린트 컴파일러 개요에 따르면 컴파일러는 블루프린트의 변수로 클래스의 속성을 만들고, 그래프의 노드를 실행 문장으로 바꾼 뒤, 이것을 블루프린트 가상 머신(virtual machine)이 실행하는 바이트코드(bytecode)로 만들어 함수에 담습니다. 이렇게 만들어지는 클래스가 UBlueprintGeneratedClass입니다. 컴파일은 매번 새 클래스를 만들지 않고 같은 클래스 객체를 비운 뒤 다시 채우므로, 다른 곳에서 이 클래스를 가리키던 참조는 컴파일한 뒤에도 고칠 필요가 없습니다.

컴파일의 마지막 단계에서는 값을 옮기는 일이 두 번 일어납니다. 먼저 클래스의 기본값을 담은 클래스 기본 객체(Class Default Object, CDO)의 값을 옛 객체에서 새 객체로 옮기는데, 이 복사는 속성의 이름을 기준으로 이루어집니다. 다음으로 변수가 늘거나 줄어 클래스의 크기가 달라졌을 수 있으므로, 컴파일러는 이 클래스로 만든 객체를 모두 찾아 새 객체를 만들고 옛 객체의 값을 옮겨 담습니다. 이 과정을 재인스턴스화(reinstancing)라고 합니다.

레벨에 이미 배치해 둔 BP_WelcomeMessage 인스턴스가 다시 배치하지 않아도 컴파일한 뒤의 그래프로 동작하는 것은 이 재인스턴스화 덕분입니다. 그 대신 한 클래스로 만든 객체가 많을수록 컴파일할 때 찾아서 다시 만들 객체도 많아집니다.


레벨에 배치하고 실행하기

만든 블루프린트가 게임 안에서 실행되려면 레벨에 인스턴스가 있어야 합니다. 블루프린트 에디터를 닫거나 옆으로 두고 메인 에디터로 돌아와, 콘텐츠 브라우저의 BP_WelcomeMessage를 레벨 뷰포트로 끌어다 놓습니다. 이 액터에는 메시 컴포넌트가 없으므로 뷰포트에서 찾기 어려울 수 있는데, 그럴 때는 아웃라이너(Outliner)에서 BP_WelcomeMessage가 추가되었는지 확인합니다.

인스턴스를 배치한 뒤에는 레벨도 저장합니다. 블루프린트 에셋을 저장하는 것과 레벨을 저장하는 것은 별개라서, 레벨을 저장하지 않으면 에디터를 다시 열었을 때 배치가 사라집니다. File 메뉴의 Save Current Level로 저장할 수 있습니다.

이제 메인 에디터 툴바의 Play 버튼을 눌러 에디터에서 플레이(Play In Editor, PIE)를 시작합니다. Print to Screen이 켜져 있다면 화면 왼쪽 위에 나 혼자 언리얼 블루프린트 시작!이 Duration에 정한 시간 동안 표시됩니다. Print to Log도 켜져 있으면 Window 메뉴에서 연 Output Log 창에도 같은 문구가 남으므로, 화면 메시지를 놓쳤을 때도 실행이 Print String까지 도달했는지 확인할 수 있습니다.


메시지가 보이지 않을 때

메시지가 나오지 않으면 실행 순서를 거꾸로 따라가며 확인합니다. 먼저 지금 플레이하는 월드에 BP_WelcomeMessage 인스턴스가 실제로 있는지 아웃라이너에서 봅니다. 다른 레벨을 열어 두었거나 레벨을 저장하지 않고 다시 열었다면 인스턴스가 없을 수 있습니다. 다음으로 블루프린트에 컴파일 오류가 없는지, BeginPlay에서 Print String까지 흰 실행 선이 이어졌는지 확인합니다. 마지막으로 Print String의 Print to Screen이 꺼져 있지는 않은지, Duration이 너무 짧지는 않은지 출력 옵션을 봅니다.

문구를 바꿨는데 이전 문구가 나온다면 컴파일을 하지 않았을 가능성이 큽니다. 문구를 바꾸고 Compile과 Save를 한 뒤 플레이를 멈췄다가 다시 시작해 바뀐 문구가 나오는지 확인해 보세요. 이 확인이 끝나면 에셋, 배치된 인스턴스, 실행 이벤트를 구분하는 첫 실습을 마친 것입니다.


정리

  • 콘텐츠 브라우저에서 Blueprint Class를 고르고 부모 클래스로 Actor를 선택해 Actor 블루프린트를 만듭니다.
  • 새 Actor 블루프린트의 이벤트 그래프에는 BeginPlay, ActorBeginOverlap, Tick 이벤트가 흐리게 미리 놓여 있습니다.
  • BeginPlay는 레벨에 있는 액터 인스턴스가 플레이를 시작할 때 실행되며, 에셋 파일이 실행되는 것은 아닙니다.
  • Print String은 개발 중 문구를 화면과 Output Log에 출력하는 노드이며, Shipping 빌드에서는 출력하지 않습니다.
  • 기본 작업 흐름은 그래프 작성, Compile, 에셋 Save, 레벨 배치와 레벨 저장, Play 순서입니다.

연습 문제

1. 인스턴스 두 개의 출력 횟수

같은 BP_WelcomeMessage 에셋으로 만든 인스턴스 두 개만 현재 레벨에 배치했습니다. 두 인스턴스 모두 BeginPlay에 한 번씩 도달하고 Print String까지 이어지며, 출력이 빠지지 않는다고 가정합니다. Play를 시작하면 메시지는 모두 몇 번 출력될까요? 에셋이 하나라는 이유로 한 번만 실행될까요?

해설 보기

모두 두 번 출력됩니다. 에셋 하나가 같은 로직을 정의하더라도, 실행은 레벨에 놓인 인스턴스마다 따로 일어나기 때문입니다. 다만 이 조건만으로는 두 출력 가운데 어느 인스턴스의 것이 먼저 나오는지 정할 수 없습니다.

확인해 보려면 한 인스턴스를 지우고 레벨을 저장한 뒤 다시 Play해 한 번만 나오는지 비교합니다. 화면 메시지가 겹쳐서 세기 어렵다면 Output Log에 남은 줄 수로 실제 실행 횟수를 확인합니다.

2. 저장했는데 메시지가 없다

블루프린트를 만들고 Print String을 연결한 뒤 Compile과 Save를 했습니다. 그런데 Play를 눌러도 메시지가 나오지 않습니다. 레벨 뷰포트로 끌어다 놓는 단계를 건너뛰었다면, 무엇이 빠진 것일까요?

해설 보기

레벨에 액터 인스턴스가 없습니다. Compile은 그래프를 실행 가능한 형태로 바꾸고 Save는 에셋을 디스크에 보존할 뿐, 월드에 인스턴스를 만들지는 않습니다.

BeginPlay는 플레이하는 월드에 있는 인스턴스가 받는 이벤트이므로, 콘텐츠 브라우저에서 BP_WelcomeMessage를 레벨로 끌어다 놓고 레벨을 저장한 뒤 다시 Play해야 합니다.

3. 메시지를 한 줄로 유지하기

인스턴스 두 개가 있는 상태에서 화면에 메시지가 한 줄만 보이게 하고 싶습니다. Print String의 어떤 입력을 쓰면 될까요? 이때 Output Log에는 몇 줄이 남을까요?

해설 보기

Key 입력에 같은 이름을 넣으면 됩니다. 같은 키로 출력된 화면 메시지는 이전 메시지를 대체하므로, 두 인스턴스가 차례로 출력해도 화면에는 마지막 한 줄만 남습니다.

Key는 화면 메시지에만 적용되므로, Print to Log가 켜져 있다면 Output Log에는 두 인스턴스의 출력이 모두 두 줄로 남습니다. 그래서 실제 실행 횟수는 로그로 확인하는 편이 정확합니다.

Print String의 입력 항목은 언리얼 엔진 공식 문서의 Print String 노드 설명에서 확인할 수 있습니다.

다음 글 블루프린트 디버깅 기초에서는 브레이크포인트와 디버그 오브젝트 선택으로, 실행이 어느 노드까지 도달했고 그때 값이 무엇이었는지 확인하는 방법을 익힙니다.