1. 중앙의 창
- 비주얼 디자이너
- 게임 내 UI 요소들을 배치하는 시각적 도구
- Screen Size를 바꾸어서 보이는 화면 크기를 바꿔볼 수 있음
- UI 구성 작업에는 위젯이 필요하고 위젯은 Palette에서 가져올 수 있음
2. Palette
- Button, CheckBox, Slider 등의 위젯을 확인하고 사용할 수 있음
3. Hierarchy
- 레벨 에디터 뷰포트의 월드 아웃라이너와 비슷함
- 각 부모 관계와 상속 관계를 확인할 수 있음
4. 애니메이션 창
- UI 위젯 애니메이션의 키프레임이나 드라이브 관련 작업을 할 수 있음
5. 디테일 창
- 각 요소의 프로퍼티를 조정할 수 있음
일반 위젯을 바로 배치할 수도 있지만, 패널 위젯(Horizontal Box, Vertical Box 등) 내에 배치하는 것이 훨씬 깔끔하고 쉽다.
UMG에서 위젯에 표시할 값을 다른 블루프린트의 프로퍼티와 연결해주는 방법
1. UMG Graph탭에서 Construct Event를 이용하여 Character Reference 변수를 만든다.
2. Progress Bar 디테일 창에서 Percent 우측에서 값을 바인딩한다.
아래 사진처럼 Character Reference 변수를 통해 캐릭터 블루프린트의 프로퍼티에 접근할 수 있다.
바인딩 완료된 화면
이렇게 하면 되게 간단하게 Progress Bar의 Percent에 표시할 값을 연결할 수 있다.
이제 Player Character에서 바인딩 된 값이 업데이트 되면 자동으로 Percent에 반영이 될 것이다.
여기 예제에서는 아래 사진과 같이 Player Character의 Blueprint에서 'BeginPlay' 이벤트에서 해준다.
시퀀스 노드도 처음 써보았는데, 어떤 하나의 노드에 대해 여러 개의 실행핀을 연결해야 할 때 유용하게 쓸 수 있겠다.
좌측 상단에 Player의 상태 정보가 뜨고,
점프하면 Energy 의 Progress Bar 값이 업데이트되는 것까지 했다.
다른 위젯 블루프린트의 자손으로 활용할 UMG의 경우 앱솔루트 레이아웃인 Canvas Panel이 필요없다.
일반 위젯 중 Image에 설정될 리소스는 Brush 타입이다.
이 예제에서는 (아마 이 UMG인 인벤토리 슬롯에 설정될 이미지가 변수에 따라 바뀔 것이기 때문에) Bind를 생성해서 이미지를 바꿀 수 있게 해주었다.
위 사진에서 PickupImage 변수는 Texture2D 타입인데 바로 Brush로 타입이 될 수는 없어서 'Make Brush from Texture' 노드를 활용하고 있다.
(Width와 Height는 썸네일 이미지에서 32x32로 해두었기 때문에 여기에선 0으로 둬도 된다.)
Button의 Style 설정에서 Normal, Hovered, Pressed 등 버튼의 상태에 따라 Tint 색상이나 Sound와 같은 것들을 설정해줄 수 있다.
버튼 상태에 따른 사운드까지 여기에서 한 번에 설정해줄 수 있다는 게 매우 간편해보인다.
Event Dispatcher는 현재 이벤트와 관련이 있는 블루프린트에 신호를 보낼 수 있는 기능이다. Blueprint Interface와는 또 다르게 블루프린트 간의 소통을 중재할 수 있는 기능이다.
아래 사진과 같이 Event Dispatcher를 만들어서 Button의 OnClicked 이벤트에 등록해줄 수 있다.
InventorySlot은 Integer형 변수로 인벤토리 내 여러 개의 슬롯 중 클릭된 슬롯이 무엇인지 전달할 용도이다.
Canvas Panel 내에서 앵커는 화면의 사이즈가 바뀌어도 위젯과 UI의 위치를 원하는 곳에 나타내기 위해 위해 필요한 중요한 개념이다. 완전 완전 중요.
UMG: 앵커 공식 문서
해당 예제에서 인벤토리는 특정 키를 눌렀을 때 표시되게 하기 위해 기본 상태는 비활성 상태이다. 이를 제어하기 위한 변수를 바인딩해주는 방법을 배웠다.
먼저 활성 상태를 제어하기 위해 bool 타입의 'ActivateInventory' 변수를 만들었다.
또한 비활성 상태에서는 눈에 보이지 않아야 하기 때문에 Visibility 속성도 같이 설정해주기 위해 ESlate Visibility 타입의 변수인 'InventoryVisible'도 만들어주고 각각 바인딩해주었다.
ESlate Visibility 타입은 처음 들어보는데, 디테일 탭이 위 사진처럼 생겼다. UMG가 Slate 기반이기 때문에 사용할 수 있다고 한다.
(여기에서는 기본값을 Hidden으로 설정해주었다.)
IsVariable 속성은 기본적으로 체크 해제되어 있는데 체크해주어야 다른 곳에서도 접근이 가능하다.
참고로 Hierarchy에서 IsVariable이 체크되어 있는 위젯은 대괄호 [ ] 표시가 없다.
배운 점
UI나 위젯 간의 렌더링 순서를 제어하기 위해서는 'ZOrder' 프로퍼티를 이용하면 된다. (값이 높을 수록 나중에 렌더링 됨)
버튼의 텍스트가 상황에 따라 변경되어야 하는 경우 Text 타입의 변수를 만들어서 바인딩해주면 된다.
Inventory에 대한 배열 관리를 Player Controller 에서 해주는 게 더 나은 이유는 Player Character 블루프린트에서 할 경우 캐릭터가 죽으면 새 캐릭터를 스폰해도 인벤토리가 이미 지워지기 때문이다.
인벤토리 업데이트 블루프린트
인벤토리 업데이트 시 수행될 내용들을 블루프린트로 작성했다.
위에서 Slot0~4는 Inventory Slot이라는 커스텀 위젯이다.
'pickupImage'는 InventorySlot에 있는 Texture2D 변수이다.
InventorySlot의 Image위젯 프로퍼티 중 Brush에 바인딩 된 함수인 GetInventoryImage를 통해 설정된다.
- 인벤토리가 업데이트 될 때 현재 플레이어가 가진 Inventory 구조체 배열의 Length와 비교해서 값이 있는 경우에만 이미지를 표시해준다.
- 그리고 딱 한 번 OnClick 이벤트를 할당해서, 버튼이 클릭될 때마다 이벤트가 발생하도록 해준다.
- 구조체 배열의 값들을 조회하기 위해서 '구조체 분해' 노드를 사용한다는 점을 배웠다.
- 참조. 아래 사진이 아까 만들었던 Inventory 구조체이다.
인벤토리 슬롯 클릭 시 실행될 이벤트 스크립트를 작성했다.
원래 스크립트까지 다 첨부할 생각은 없었는데, 블루프린트 흐름과 각 역할을 제대로 이해하려면 하나하나 정리하면서 이해하는 게 좋을 것 같다.
인벤토리 슬롯이 클릭되면,
- 인벤토리 메뉴를 비활성화 시킨다. Visibility는 그대로이므로 눈에 보이겠지만 액션 메뉴가 활성화 되어있을 동안 무언가 클릭할 수는 없도록 해서 예외상황 발생을 막는다.
- InventorySlotClicked 라는 Integer 변수를 만들어서 현재 선택된 슬롯의 인덱스를 참조한다.
- 또한 현재 선택된 슬롯의 인덱스를 통해 Inventory 구조체 배열에서 해당되는 값을 가져온다.
- 현재 선택된 슬롯 아이템에 알맞는 Action Text를 설정한다.
- 그리고 마지막으로 모두 설정이 끝나면 ESlate Visibility 변수인 'ActionMenuVisible'을 'Visible'로 설정해서 액션 메뉴가 보이도록 한다.
Action Menu 중 Cancel 버튼의 OnClick 이벤트 스크립트를 작성했다.
여기에서 배울 점은 'ActionComplete'라는 커스텀 이벤트를 만든 것이다.
Cancel 버튼 외에 Drop 버튼이나 Use 버튼을 눌렀을 때에도 해당 작업 후 인벤토리 메뉴를 다시 활성화시키고, 액션 메뉴를 숨기는 작업이 동일하게 필요하다.
이럴 때 두 개의 SET 이벤트를 매번 만들기보다 커스텀 이벤트를 만들어두고 그 이벤트만 호출하면 되도록 한 것이다.
블루프린트는 안그래도 복잡하기 때문에 커스텀 이벤트를 잘 활용하면 중복 노드를 줄이면 좋을 것 같다.
Blueprint Interface를 사용하는 방법
- 이벤트 그래프 상단에서 'Class setting'을 클릭한다.
- 디테일 탭에서 구현된 인터페이스에서 아까 만들어뒀던 'Action Interface'를 선택한다.
- 그래프에서 Drop Action을 검색하는데 'Context Sensitive' 를 체크 해제하면 보이는 'Drop Action(메시지)' 를 추가한다.
블루프린트 인터페이스인 'Action Interface'에는 아래와 같이 UseAction, DropAction 함수를 만들어뒀었다.
Action Menu 중 Drop Button 의 OnClick 이벤트 스크립트를 작성했다.
- Drop을 하면서 캐릭터의 인벤토리에 접근하기 위해 Character Reference 변수를 사용하였다.
이 변수는 FirstPersonCharacter 타입인데 해당 블루프린트에서 플레이 시작 시 GameHUD를 만들어주고 있기 때문에 참조한 것이다.
- DropAction 노드에 편지봉투가 달려있는 걸 볼 수 있는데 이건 Blueprint Interface 라는 의미이다.
- 플레이어가 가진 Inventory 배열에서 삭제해주기 위해 현재 선택된 슬롯의 인덱스가 필요했는데, Slot 클릭 시 설정되는 'InventorySlotClicked' 변수를 사용할 수 있다.
- 아까 만들어 둔 Refresh Inventory 라는 이벤트를 사용해서 변경된 배열에 대해 UI를 업데이트 해줄 수 있다.
- 또 아까 만들어 둔 커스텀 이벤트인 Action Complete를 통해 UI를 정리할 수 있다.
- 사소하지만 나중에 헷갈릴 것 같은 건 Inventory 배열을 get으로 가져온 후에도 또 'GET'이라는 참조 노드를 사용한다는 점이다. 이건 아직 이유를 모르겠다.
Action Menu 중 Use Button의 OnClick 이벤트 스크립트를 작성했다.
- 스크립트의 대부분이 Drop Button 이랑 똑같다.
- Drop Action 을 Use Action으로 바꿨고, Character Reference를 없앴다.
- Target으로는 선택된 Item을 넘겨주었는데, Blueprint Interface 쪽에 Target을 추가해준 게 없어서 어떻게 저게 생겨있고, 어떻게 Inventory Structure 타입에서 Item을 바로 연결해줄 수 있는지 아직 이해 안된다.
배운 점
- 위 사진은 FirstPersonCharacter에 있는 블루프린트이다.
- GameHUDReference는 BeginPlay 시 Game HUD를 만들 때 캐싱한 변수이다.
- [SetInputModeGameAndUI]는 마우스 커서를 활성화할 수 있는 노드이다.
- PlayerController에서 할당된 HUD를 가져온 다음 그게 First Person HUD인지 확인하기 위해 Casting을 진행한다.
- 그리고 크로스헤어는 비활성화, 커서는 활성화한다.
- [SetInputModeGameOnly] 노드를 사용하였다.
- I 키를 누르면 현재 GameHUDReference에서 인벤토리 메뉴가 활성 상태인지 확인한다.
활성상태이면 Inventory Visible 변수를 Visible로 설정한다.
이 때 해당 변수는 GameHUD에서 Inventory 메뉴의 Visibility 에 바인딩 되어 있기 때문에 자동으로 메뉴가 보이게 된다. 그리고 이 메뉴를 클릭할 수 있도록 함수 호출을 통해 마우스 커서를 활성화 해준다.
- I키를 눌렀을 때 이미 활성상태이면 Hidden으로 설정하여 메뉴를 비활성화한다. 그리고 다시 마우스 커서를 비활성화 해준다.