위젯 블루프린트의 기본 개념
UMG와 위젯 블루프린트의 디자이너·그래프 구조를 이해하고 기본 UI를 생성해 화면에 표시합니다.
게임 로직이 아무리 정확해도, 플레이어는 화면에 보이는 것으로만 게임의 상태를 알 수 있습니다. 체력이 얼마나 남았는지, 지금 몇 점인지, 어떤 버튼을 눌러야 게임이 시작되는지를 알려 주는 것이 사용자 인터페이스(User Interface, UI)입니다. 그런데 처음 UI를 만들어 보면 에디터에서는 버튼이 잘 보이는데 게임 화면에는 나타나지 않거나, 버튼은 보이는데 마우스로 누를 수 없는 일이 자주 생깁니다.
언리얼 엔진은 UI를 만들기 위해 위젯 블루프린트(Widget Blueprint)를 제공합니다. 이 글에서는 위젯 블루프린트가 무엇이고 에디터가 어떻게 구성되어 있는지 살펴본 뒤, 메인 메뉴의 "게임 시작" 버튼을 만들어 화면에 띄우고 클릭에 반응하게 해 봅니다. 그 과정에서 위젯을 만드는 일, 화면에 추가하는 일, 입력을 UI로 돌리는 일이 서로 다른 단계라는 점을 확인합니다.
게임 화면의 UI가 하는 일
UI는 플레이어가 게임과 소통하고 정보를 얻는 시각적이고 상호작용적인 요소 전부를 가리킵니다. 체력 바, 점수 표시, 인벤토리 창, 메인 메뉴 버튼, 설정 화면, 퀘스트 로그처럼 게임 화면 위에 나타나는 요소 대부분이 여기에 들어갑니다.
UI는 먼저 게임의 상태와 목표, 진행 상황을 플레이어에게 전달합니다. 또 플레이어가 메뉴를 탐색하고 아이템을 쓰거나 스킬을 발동하는 것처럼 게임을 조작하는 통로가 됩니다. 잘 만든 UI는 게임의 세계관과 분위기를 살려 몰입을 돕고, 직관적이고 반응이 빠른 UI는 플레이어가 게임을 쉽게 이해하게 해 사용자 경험(UX)을 좋게 만듭니다.
위젯 블루프린트와 UMG
UMG(Unreal Motion Graphics)는 언리얼 엔진에서 UI를 만들고 배치하고 시험해 보는 시각적 UI 도구이고, 위젯 블루프린트는 UMG에서 UI 하나를 정의하는 블루프린트입니다. 위젯 블루프린트는 게임 화면에 띄울 창이나 패널의 설계도라고 생각하면 됩니다. 이 설계도 안에 버튼, 텍스트 같은 UI 구성 요소를 배치하고, 그 요소들이 어떻게 동작할지를 정의합니다.
위젯 블루프린트가 정의하는 것은 세 가지입니다. 첫째는 버튼, 텍스트 블록, 이미지, 슬라이더, 스크롤 박스 같은 구성 요소의 배치와 모양, 즉 레이아웃입니다. 둘째는 버튼을 눌렀을 때 일어날 일이나 슬라이더 값이 바뀌었을 때의 동작, 조건에 따라 요소를 보이거나 숨기는 처리 같은 상호작용 로직입니다. 셋째는 게임의 값을 위젯의 속성으로 읽어 표시하는 데이터 바인딩(data binding)입니다.
데이터 바인딩은 편리하지만 동작 방식을 알고 써야 합니다. 일반적인 속성 바인딩은 값이 바뀌었다는 알림을 받아 실행되는 것이 아니라, 위젯이 화면에 그려지는 동안 반복해서 바인딩 함수를 평가합니다. 그래서 위젯이 많아지면 그만큼 평가 비용이 쌓이므로, 체력처럼 가끔 바뀌는 값은 상태가 바뀌는 이벤트에서 표시 함수를 직접 호출하는 방법도 함께 씁니다. 이 방식은 UI와 게임플레이 기본 상호작용에서 다시 다룹니다.
위젯을 설계할 때는 두 흐름을 나누어 두면 구조가 분명해집니다. 하나는 게임의 상태를 화면에 표시하는 흐름이고, 다른 하나는 플레이어의 입력을 게임 로직으로 전달하는 흐름입니다. 체력 바는 앞의 흐름에, "게임 시작" 버튼은 뒤의 흐름에 속합니다.
디자이너 탭과 그래프 탭
위젯 블루프린트 에디터를 열면 오른쪽 위에서 두 탭을 오갈 수 있습니다. 디자이너(Designer) 탭은 UI의 모양을, 그래프(Graph) 탭은 UI의 동작을 만드는 곳입니다.
디자이너 탭에는 네 영역이 있습니다. 팔레트(Palette)는 버튼, 텍스트, 이미지, 패널처럼 미리 정의된 UI 위젯을 모아 둔 곳으로, 여기서 위젯을 끌어다 작업 영역에 놓아 UI를 구성합니다. 계층 구조(Hierarchy)는 추가한 위젯들의 부모와 자식 관계를 보여 주므로, 어떤 위젯이 어떤 패널 안에 묶여 배치되는지 파악할 수 있습니다. 가운데의 디자인 작업 영역은 Canvas Panel 같은 루트 패널 안에 레이아웃을 구성하는 공간으로, 위젯을 놓거나 크기, 위치, 정렬을 조절합니다. 디테일 패널(Details)에서는 선택한 위젯의 크기, 색상, 텍스트 내용, 정렬 방식 같은 속성을 정합니다.
디테일 패널의 Is Variable 옵션은 따로 알아 둘 만합니다. 이 옵션을 켜면 그 위젯이 위젯 블루프린트의 변수가 되어 그래프에서 참조할 수 있습니다. 버튼의 클릭 이벤트를 만들거나 텍스트의 내용을 바꾸려면 그 위젯을 그래프에서 다룰 수 있어야 하므로, 로직에서 쓸 위젯은 이 옵션을 켜 둡니다.
그래프 탭의 이벤트 그래프(Event Graph)는 액터 블루프린트의 이벤트 그래프와 비슷하게, 버튼 클릭, 슬라이더 값 변경, 애니메이션 재생 완료처럼 UI에서 일어나는 이벤트에 반응하는 노드를 연결하는 곳입니다. UI 로직에 필요한 함수, 변수, 매크로도 여기서 만들고 관리합니다.
게임 시작 버튼 만들어 띄우기
가장 기본적인 UI인 메인 메뉴 버튼을 하나 만들어 화면에 띄우고, 클릭하면 메시지가 출력되게 해 봅니다. 작업은 세 곳에서 나눠 합니다. 디자이너 탭에서 버튼의 모양을 만들고, 같은 위젯 블루프린트의 그래프 탭에서 클릭했을 때의 동작을 연결한 뒤, 다른 블루프린트에서 위젯을 만들어 화면에 추가합니다. 앞의 두 단계만 마치고 플레이하면 아무것도 보이지 않는데, 위젯은 마지막 단계에서 누군가 만들어 화면에 붙여야 나타나기 때문입니다.
위젯 블루프린트 만들고 버튼 배치하기
콘텐츠 브라우저의 빈 곳을 오른쪽 클릭하고 User Interface 하위 메뉴에서 Widget Blueprint를 선택합니다. 부모 클래스를 고르는 창이 뜨면 User Widget을 고르고, 이름을 WBP_MainMenu로 짓습니다. 위젯 블루프린트에는 흔히 WBP_ 접두사를 붙입니다.
WBP_MainMenu를 더블클릭해 에디터를 엽니다. 새로 만든 위젯 블루프린트에는 루트 패널이 없을 수 있으므로, 없다면 팔레트에서 Canvas Panel을 먼저 추가합니다. 그 안에 Button을 놓고 앵커와 정렬을 맞춘 뒤, 팔레트의 Text를 버튼 위로 끌어다 놓아 버튼의 자식으로 만듭니다. 텍스트 위젯을 선택해 디테일 패널의 Text 속성을 "게임 시작"으로 바꾸고, 버튼 위젯을 선택해 Is Variable을 켠 뒤 이름을 StartGameButton으로 정합니다.
클릭 이벤트 연결하기
그래프 탭으로 이동해 마이 블루프린트(My Blueprint) 패널의 변수 목록에서 StartGameButton을 선택합니다. 디테일 패널을 아래로 내리면 이벤트(Events) 영역이 있는데, 여기서 On Clicked 옆의 + 버튼을 누르면 On Clicked (StartGameButton) 이벤트 노드가 만들어집니다. 이 노드 뒤에 Print String을 연결해 "게임 시작 버튼 클릭됨!"을 출력하게 합니다. 실제 메뉴라면 여기에 Open Level (by Name)을 연결해 게임 레벨로 이동하게 할 수도 있습니다.
위젯을 만들어 화면에 추가하기
위젯 블루프린트는 만들어 두기만 해서는 화면에 나타나지 않습니다. 다른 블루프린트가 실행 중에 위젯 인스턴스를 만들고 화면에 추가해야 합니다. 여기서는 레벨 블루프린트나 BP_PlayerController를 열어 Event BeginPlay 뒤에 다음 노드를 차례로 연결합니다.
먼저 Create Widget 노드의 Class에 WBP_MainMenu를 고르고, Owning Player에는 Get Player Controller로 얻은 컨트롤러를 연결합니다. 공식 문서에 따르면 Owning Player는 위젯을 소유할 플레이어 컨트롤러이며, 비워 두면 기본 플레이어 컨트롤러가 쓰입니다. Create Widget이 돌려준 Return Value는 MainMenuRef 같은 변수에 저장해 둡니다. 나중에 메뉴를 닫을 때 이 참조가 필요하고, 이미 만든 메뉴를 또 만드는 중복 생성도 막을 수 있습니다.
다음으로 Add to Viewport의 Target에 그 위젯 참조를 연결합니다. 이 노드가 위젯을 실제로 화면에 그리게 합니다. 이어서 Set Input Mode UI Only로 입력을 UI에 돌리는데, 같은 로컬 플레이어 컨트롤러를 Player Controller 핀에 연결하고 포커스할 위젯으로 메뉴를 지정합니다. 마지막으로 그 컨트롤러를 Target으로 Set Show Mouse Cursor를 실행하고 값을 켜서 마우스 커서가 보이게 합니다. 컴파일하고 저장한 뒤 플레이하면 화면에 "게임 시작" 버튼이 나타나고, 클릭하면 메시지가 출력됩니다.
메뉴를 닫고 게임으로 돌아갈 때는 이 과정을 거꾸로 되돌립니다. Remove from Parent로 위젯을 화면에서 떼어 내고, Set Input Mode Game Only 같은 게임용 입력 모드를 다시 설정하고, 마우스 커서를 숨깁니다. 셋 중 하나라도 빠뜨리면 메뉴는 사라졌는데 캐릭터가 움직이지 않거나 커서가 남는 일이 생깁니다.
Construct와 OnInitialized
위젯 블루프린트의 그래프에는 위젯이 준비될 때 실행되는 이벤트가 두 가지 있습니다. Event Construct는 위젯이 화면 계층에 추가될 때 실행되므로, 같은 위젯을 뗐다가 다시 붙이면 다시 실행될 수 있습니다. 반드시 한 번만 해야 하는 초기화는 위젯 객체가 처음 만들어질 때 실행되는 Event On Initialized처럼 역할에 맞는 이벤트에 둡니다.
정리
- 위젯 블루프린트는 UMG에서 UI 하나의 레이아웃, 상호작용 로직, 데이터 바인딩을 정의하는 블루프린트입니다.
- 디자이너 탭에서는 팔레트, 계층 구조, 작업 영역, 디테일 패널로 모양을 만들고, 그래프 탭에서는 이벤트에 반응하는 로직을 만듭니다.
- 그래프에서 다룰 위젯은
Is Variable을 켜 두어야 하며, 버튼의On Clicked같은 이벤트는 디테일 패널의 이벤트 영역에서 추가합니다. - 위젯은
Create Widget으로 만들고Add to Viewport로 화면에 추가하며, 버튼을 누르려면 입력 모드와 마우스 커서도 따로 설정해야 합니다. - 일반 속성 바인딩은 반복해서 평가되고,
Event Construct는 화면에 다시 추가될 때마다 실행될 수 있습니다.
연습 문제
1. 만든 위젯과 보이는 위젯
Create Widget이 성공해 MainMenuRef에 저장했지만 Add to Viewport는 실행하지 않았습니다. 디자이너에서 보이던 버튼을 게임 화면에서도 클릭할 수 있을까요? 또 메뉴를 닫을 때 Remove from Parent만 호출했다면 게임 조작의 복구도 끝난 것일까요?
해설 보기
첫 조건에서는 위젯 인스턴스가 만들어지기만 했고 화면에 추가되지 않았으므로 클릭할 수 없습니다. 디자이너에 보이는 모습은 설계도의 미리보기이지 게임 화면이 아닙니다.
둘째 조건에서는 위젯을 화면에서 뗀 것만 확인했습니다. 메뉴를 띄울 때 Set Input Mode UI Only를 설정했다면 Set Input Mode Game Only 같은 필요한 입력 모드와 커서 상태도 되돌려야 게임 조작이 돌아옵니다. 실습에서는 생성, 참조 저장, 화면 추가, 입력 모드와 커서 설정을 같은 로컬 컨트롤러에서 차례로 확인합니다.
2. 클릭 이벤트를 찾을 수 없음
디자이너에서 버튼을 배치하고 그래프 탭으로 갔는데, 변수 목록에 버튼이 없어 On Clicked 이벤트를 추가할 수 없습니다. 무엇을 빠뜨렸을까요?
해설 보기
버튼 위젯의 Is Variable 옵션을 켜지 않았을 가능성이 큽니다. 이 옵션이 꺼진 위젯은 위젯 블루프린트의 변수로 만들어지지 않으므로, 그래프의 변수 목록에 나타나지 않습니다.
디자이너 탭에서 버튼을 선택해 Is Variable을 켜고, 알아보기 쉬운 이름(예: StartGameButton)을 붙인 뒤 컴파일합니다. 그러면 변수 목록에 버튼이 나타나고, 디테일 패널의 이벤트 영역에서 On Clicked를 추가할 수 있습니다.
3. 메뉴를 열 때마다 초기화가 반복됨
설정 메뉴를 열 때 Add to Viewport, 닫을 때 Remove from Parent를 호출합니다. 같은 위젯 참조를 계속 재사용하는데, Event Construct에 넣어 둔 "기본 음량을 50으로 맞추기" 처리 때문에 메뉴를 다시 열 때마다 플레이어가 바꾼 음량이 50으로 돌아갑니다. 왜 그럴까요?
해설 보기
Event Construct는 위젯이 화면 계층에 추가될 때마다 실행될 수 있기 때문입니다. 같은 위젯 객체라도 Remove from Parent로 뗐다가 Add to Viewport로 다시 붙이면 Construct가 다시 실행되어, 음량을 50으로 되돌립니다.
한 번만 해야 하는 기본값 설정은 위젯 객체가 처음 만들어질 때 실행되는 Event On Initialized로 옮깁니다. Construct에는 메뉴를 열 때마다 해야 하는 일, 예를 들어 현재 음량을 화면의 슬라이더에 표시하는 일만 남깁니다.
위젯을 만들어 화면에 추가하고 제거하는 노드는 언리얼 엔진 공식 문서의 위젯 생성에서 확인할 수 있습니다.
다음 글 동적 UI 요소 생성 및 기본 관리에서는 미리 배치해 둔 위젯 대신, 게임 중에 위젯을 만들어 패널에 추가하고 데이터가 바뀔 때 갱신하고 제거하는 방법을 다룹니다.