우리가 어떠한 3D UI 요소를 만든다면, 그것을 게임 세계에 표현할 수 있다.
우리는 'Widget Blueprint Editor' 에서 다양한 Widget를 사용해 UI를 꾸밀 수 있고 함수 기능을 추가할 수 있다.
이번에는 이 Widget를 이용해 체력바를 만들고, 캐릭터들 위에 띄워보겠다.
일단 Content Browser에서 우클릭 후 Widget Blueprint를 만들어준다.
왼쪽 탭을 보면 Palette가 보인다.
여기서 다양한 Widget 들이 있는데, 우리는 간단한 HPBar를 구현할 것이다.
이 곳에서 Vertical Box를 먼저 드래그 하여 Hierarchy에 놓은 후
Progress Bar를 드래그하여 Vertical Box 밑에 놓는다.
Progress Bar의 이름은 자신이 원하는 대로 짓고, Size를 Fill로 해주자
컬러도 자신이 원하는 색으로 한다, 문서에서는 빨간색으로 처리한다.
이제 이 HPBar를 위한 UserWidget 클래스를 만들어주어야 한다.
Content Browser에서 우클릭 후 Add C++ Classes를 하여, UserWidget을 하나 만들어준다.
이 UserWidget도 우리가 만든 Actor에서처럼 BeginPlay가 존재하는데, 이곳에서는 NativeConstructor를 사용한다.
일단 우리가 위에서 만든 Widget Blueprint에서, Progress Bar의 정보를 이름으로 가져온다.
SetPercent 함수로 Progress Bar가 채워진 정도를 정할 수 있다. [0.0, 1.0]
이제 다시 Widget Blueprint로 넘어가, 우리가 만든 Widget Class로 변경해주어야 한다.
우리가 만든 Widget Blueprint를 켜면, 우측 상단에 Graph 가 보이는데 이를 클릭한다.
그럼 이곳에 Class Settings가 있는데, 여기서 우리가 만든 UserWidget 클래스를 등록해준다.
이제 우리는 우리가 만든 UserWidget을 캐릭터 위에 띄워주고, HP가 변경될때마다 업데이트 해주기만 하면 된다.
UserWidget을 HUD(우리가 아는, 게임 상에서 2D형태로 화면에 나타나는 UI) 가 아닌 게임 월드 상에서 보여주게 하려면 WidgetComponent 라는것이 필요하다.
우리가 캐릭터를 사용하는 곳에서, WidgetComponent를 만들어준다.
여기서 CreateDefaultSuboject는 '컴포넌트'를 만들어주는 함수이다. 이때 인자로 넘어가는 FName은 액터에 속한 컴포넌트들을 식별하는데 사용된다. 다른 컴포넌트들과 이름이 중복되어선 안된다.
그리고 기능이 아닌, Transform을 가지고 있는 컴포넌트이기때문에 SetupAttachment로 Transform을 설정해주어야 한다.
이제 우리가 만든 Widget Blueprint의 Reference를 가져와, WidgetComponent에게 넘겨주면 된다.
여기서 FObjectFinder가 아닌, FClassFinder임을 주의하자
이는 위에서 AnimInstance때와 똑같은데, 우리가 어떠한 Blueprint를 만들고 해당 Parent Class를 우리가 만든 Class로 지정해주었었다. 그런 에셋들에 대해서는 해당과 같이 가져온다고 생각하면 쉽다. 에디터를 열 때 (게임을 시작할때) 생성되어 있기 때문이다.
여기서 SetWidgetSpace 함수는, 우리가 만든 Widget을 어떤 형식으로 그려줄 것인가를 정의한다.
EWidgetSpace::World는 3D 형식으로 보여준다.
EWidgetSpace::Screen은 2D 형식으로 보여준다.
이제 실제로, 체력이 변경될때 ProgressBar의 SetPercent 함수를 사용하여 체력을 변경시켜주면 된다.
해당문서에서는 다음과 같이 처리했다.
커플링을 방지하기 위해
체력이 업데이트 될때
체력이 0이 되어서 사망했을때
에 대한 Delegate를 만들고, 캐릭터의 체력이 업데이트 될 때 Delegate에 등록되어있는 모든 객체들에게 알리도록 처리하였다.
이 사진에서 주의할 점은 UserWidget이 GetOwner() 라는 함수를 가지고 있다는 점인데,
이 함수는 현재 언리얼에서 제공하지 않고 있다.
우리의 Widget이 플레이어의 정보를 알려면 2가지 방법이 있다.
1. UUserWidget과 UWidgetComponent를 상속받은 새로운 클래스에서 Actor를 저장해놓고, 가져다 쓰기
2. 엔진을 커스타미징하여 UWidgetComponent에서 받아온 GetOwner를 UUserWidget에서 저장하기
필자는 2번 방식으로 사용했다.
필자가 직접 엔진 커스터마이징을 하여 제공하고 있는 것이고, 해당 내용은 아래 문서를 참고해달라
https://openmynotepad.tistory.com/120
CharacterWidget을 CharacterBase가 아닌, IABCharacterWidgetInterface를 통해 처리하게 하여 의존성을 없앴다.
우리의 캐릭터가 데미지를 입으면, OnHPChanged Delegate를 호출한다.
이 Delegate에 위의 UpdateHPBar가 Bind 되어 있는것이 보인다.
마지막으로 알아야 할 것은, 언제 Widget이 만들어 지는것인가에 대한 것이다.
이는, InitWidget를 보면 알 수 있다.
우리가 위에서, UserWidget의 Class를 가져와 WidgetComponent에 붙여준 것을 기억할 것이다.
그 붙여준 Class를 InitWidget때 생성하게된다.
즉 WidgetComponent는 빈 껍데기일 뿐이고, UserWidget을 담는 그릇정도로 생각하면 된다.
이로써 알 수 있는것은
1. Actor가 BeginPlay가 될 때, 모든 Actor들에 대해서 BeginPlay를 호출한다.
2. 이 BeginPlay가 호출 될 때, InitWidget이 호출되는데 이 때 Widget이 생성된다.
결론은
Actor-BeginPlay
WidgetComponent-InitWidget
UserWidget-NativeConstruct
순으로 호출된다.
이를 기억해두자
'UnrealEngine > 기능' 카테고리의 다른 글
[Unreal Engine 5] Singleton (0) | 2023.10.02 |
---|---|
[Unreal Engine 5] Asset Manager (0) | 2023.10.01 |
[Unreal Engine 5] 공격 판정 (0) | 2023.09.23 |
[Unreal Engine 5] Animation Montage (0) | 2023.09.21 |
[Unreal Engine 5] 캐릭터 애니메이션 (1) | 2023.09.19 |