Udemy 강의인 Unreal Engine C++ The Ultimate Game Developer Course 를 바탕으로 작성하였습니다.
Health Bar를 만들어보면서 UMG를 사용해 볼 예정입니다.
1.UMG
언리얼 엔진에서는 UMG(Unreal Motion Graphics)라는 것이 있는데 아래는 언리얼 엔진 공식 문서에 작성된 UMG에 대한 설명이다.
언리얼 모션 그래픽 UI 디자이너 (UMG) 는
게임내 HUD, 메뉴, 기타 인터페이스 관련 그래픽 요소로 사용자에게 보여주고픈 것들을 만드는 데 사용할 수 있는
비주얼 UI 제작 툴입니다.UMG 의 핵심에는 Widget (위젯) 이라는 것이 있는데,
이는 미리 만들어진 (버튼, 체크박스, 슬라이더, 진행상황 바 등의) 함수 시리즈로, 이들을 조립해서 UI 를 만들 수 있습니다.
이러한 위젯은 전용 Widget Blueprint (위젯 블루프린트)에서 편집되는데, 두 가지 탭으로 구성됩니다.
Designer (디자이너) 탭에서는 인터페이스의 시각적인 레이아웃과 기본적인 함수가 제공되며,
Graph (그래프) 탭에는 사용된 위젯 내부의 함수 기능이 제공됩니다.
1-1. C++에서 UMG 사용하기
C++ 코드에서 UMG 관련 기능들을 사용하기 위해서는 모듈을 등록해야 한다.
파일 이름은 프로젝트명.Build.cs
로 C# 파일로 구성되어 있다.
위 공식 문서를 확인해보면 PublicDependencyModuleNames
, PrivateDependencyModuleNames
에 모듈을 추가하는데
언리얼의 소스 코드 구조는 일반적으로 Public에는 헤더 파일을
Private에는 소스 파일을 넣어서
PublicDependencyModuleNames: 헤더파일이 참고할 모듈
PrivateDependencyModuleNames: 소스코드에서만 참고할 모듈
이라 생각하면 된다.
개인적인 정리로 PublicDependencyModuleNames은 .h 파일이고, PrivateDependencyModuleNames은 위에서 추가한 .h 파일의 .cpp 파일이라고 정리했다.
해당 내용에 좀 더 좋은 정리나 의견이 있으시면 공유해주세요!!
이제 코드에 UMG 모듈을 추가해보자.
1
2
3
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" , "UMG"});
PrivateDependencyModuleNames.AddRange(new string[] { "Slate" , "SlateCore" });
2. Widget Blueprint 만들기
Content Browser
에서 Widget Blueprint를 생성할 수 있다.
여기서 각자 원하는 UI를 세팅하면 되는데 왼쪽에 Palette
창을 이용해서 원하는 기능을 추가하면 된다.
저 같은 경우는 아래와 같이 세팅했다.
- 하이어라키 창을 통해 좀 더 세밀한 세팅을 할 수 있다.
- 왼쪽 상단에 빨간 게이지는 2-1 내용까지 포함되어 있는 것이다.
2-1. HealthBar 용 Widget 만들기
기본적인 HUD를 만들고 나서는 Health Bar를 만들어보자.
위와 동일하게 Wdiget Blueprint를 만들고, 이름을 Health Bar 라고 만들자.
하이어라키에 Horizontal Box를 추가하고 자식으로 ProgressBar을 추가하자.
그리고 우측 Details 창에서 Size를 Fill
로 설정하면 Horizontal Box
에 가득 차게 설정할 수 있다.
기본적인 컬러 설정 후 Detail 창 내 Progress
의 Percent
를 통해 Progress Bar
값을 설정할 수 있는데,
옆의 Bind
를 통해서 커스텀 함수를 묶어 Percent 값을 조절할 수 있다.
여기서 값은 캐릭터의 HP가 되기 때문에 이를 설정해주면 된다.
2-2. Progress Bar의 Percent 값 Bind 만들기
Create Binding
를 누르면 다른 창으로 이동하는데, 다시 전으로 돌아가려면 우측 상단을 통해 돌아갈 수 있다.
Graph
창에서 새로운 Functions
을 만들 수 있는데 현재 보이는 노드들은 아래와 같이 이해하면 된다.
여기서 변수를 하나 생성하고 난 뒤, 변수의 자료형을 현재 플레이어 캐릭터의 BluePrint로 설정한다.
2-3. IsValid를 통해 유효성 검사
위에서 만든 변수는 코드로 보면 아래와 같다.
1
MyPlayerCharacterBP* variable;
그렇기 때문에 현재 변수는 null 값이 들어가 있고, 값이 들어가 있는지 확인을 하기 위해 IsValid를 사용해야 한다.
2-4. nullptr / IsValid
여기서 nullptr과 IsValid의 차이를 확인해보자
- nullptr: null 상태
- IsValid: Pending Kill 상태까지 검사
Pending Kill 이란, 액터를
Destroy()
를 호출하여 소멸시킬 때, 레벨에서 제거되어 Pending Kill 상태가 되고 Pending kill 상태가 된 액터는 잠시 후 삭제가 된다.즉, Pending Kill 은 유효한 객체이지만 게임 플레이에는 유효하지 않는 상태 라 보면 된다.
2-5. 마무리
2-6. 2-1에서 만든 Widget에 붙이기
Palette
를 보면 User Created
가 보이는데 여기서 우리가 만든 Health Bar
를 볼 수 있다.
이걸 끌고 와서, 하이어라키에 넣어주자!!
3. PlayerController에 Widget 넣기
위의 내용을 전부 다 하고 나서 게임을 플레이하면 Widget이 정상적으로 노출되지 않는다.
그 이유는 당연하게도 Widget을 만들기만 했지 아직 적용하지 않았기 때문이다.
여기서 우리는 PlayerController를 통해 Widget을 붙일 것이다.
Widget을 붙이는 방법은 여러 방법이 있다고 한다. 우리는 그 중 PlayerController를 통해 Widget을 붙일 것이다.
3-1. 왜 PlayerController ??
여기서 주의깊게 봐야 하는 내용은 아래와 같다.
또한 어떤 경우에는 입력 처리나 기타 함수성을 PlayerController 에 넣는 것이 필요할 수도 있습니다.
PlayerController 는 게임 내도록 유지되는 반면, Pawn 은 휘발성이기 때문입니다.예를 들어 데스매치 스타일 게임플레이에서 죽고 리스폰되면서 Pawn 은 달라지더라도 PlayerController 는 똑같을 수 있습니다.
이 경우 점수를 Pawn 에 유지했다면 리셋되겠지만, PlayerController 에 유지한 경우에는 리셋되지 않을 것입니다.
위에서 만든 Widget 같은 경우는 캐릭터가 죽더라도 리셋되거나 변경되지 않기 때문에 PlayerController를 통해서 생성하는 것이다.
3-2. C++ 코드 작성
C++ 코드로 PlayerController를 생성 후 아래와 같이 구현하자.
.h 파일
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class RPG_PROTOTYPE_API AMyPlayerController : public APlayerController
{
GENERATED_BODY()
public:
/** HUD 에셋을 저장하는 변수 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Widgets")
TSubclassOf<class UUserWidget> WidgetAsset;
protected:
/** APlayerController Class 에 정의되어 있다. */
virtual void BeginPlay() override;
};
.cpp 파일
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void AMyPlayerController::BeginPlay()
{
Super::BeginPlay();
if(HUDOverlayAsset)
{
// CreateWidget을 통해 Widget을 생성
UUserWidget* Widget = CreateWidget<UUserWidget>(this, WidgetAsset);
if(Widget)
{
// Viewport에 띄우기
Widget->AddToViewport();
// 가시성 세팅 ( HitTestInvisible 통해 보이게 설정( 상호작용은 불가능 ) )
Widget->SetVisibility(ESlateVisibility::HitTestInvisible);
}
}
}
3-3. 마무리
위에서 만든 PlayerController CPP 파일을 Blueprint로 만든 후, WidgetAsset에 만든 Widget Blueprint를 추가하자.