2 minute read


Unreal Engine 5 - Gameplay Ability System - Top Down RPG


1. Game UI Architecture


UI가 데이터에 직접 접근하는 건 좋은 방식이 아니다.

데이터(Model)과 UI(View) 사이에 징검다리 역할을 해주는 클래스(Controller)를 따로 마련하자. Model에서 필요한 데이터를 검색해 View로 브로드캐스팅할 수 있다.


WidgetController의 역할

  • 데이터 검색 후 뷰로 전달
    • 뷰와 모델 사이의 중개자 역할
  • 데이터 처리와 관련된 계산 및 알고리즘 수행
  • UI 버튼을 통해 변경된 데이터를 모델에게 전달


ViewControllerModel

  • 단방향 종속성 형태
    • 모델은 어떤 컨트롤러나 위젯이 사용되는지 신경 X
    • 컨트롤러는 어떤 위젯이 브로드캐스트를 수신하는지 알 필요 X



2. Widget Controller

2.1. User Widget


위젯이 컨트롤러의 브로드캐스트를 수신하기 위해 위젯 클래스에 컨트롤러에 대한 멤버 변수가 있어야 한다.

특정 위젯에 대한 위젯 컨트롤러가 설정되면 함수를 호출해 해당 컨트롤러에 대한 데이터를 받아오자. 컨트롤러에서 데이터를 받아와야 블루프린트에서 대응 이벤트를 만들 수 있다.


Widget 클래스에서 만들어야 할 것

  • Widget Controller 멤버변수
  • Widget Controller Set 함수
  • Widget Controller와 함수간의 연결 (Setter)


위젯 컨트롤러는 위젯에 사용될 모델의 데이터를 가져오기 위해 TObjectPtr<클래스명> 형태의 멤버 변수를 선언한다(캐릭터 컨트롤러, 상태 등). 블루프린트에서 읽어올 수 있어야 하므로 UPROPERTY를 추가하자.


위젯 컨트롤러 클래스에 구조체를 선언해 멤버 변수를 쉽게 초기화할 수 있다. 블루프린트에서 구조체를 사용하는 경우를 대비해 구조체 지정자 USTRUCT(BlueprintType)를 추가한다.


AuraWidgetController.h


2.2. Overlay Widget Controller


Health/Mana UI는 오버레이 위젯을 상속받은 BP를 통해 Viewport에 띄운다. 게임을 시작하면 위젯 컨트롤러에 속성값을 초기화시키고 위젯을 Viewport에 추가해야 한다.

위젯 컨트롤러의 초기화 및 적용은 HUD 클래스에 구현한다. Overlay Widget Controller를 싱글톤으로 관리하고 위젯의 속성값을 초기화하는 과정을 HUD 클래스에 작성한다. 오버레이 위젯을 초기화하는 함수를 만들어 오버레이 위젯이 필요한 속성값을 제공한다.


AuraHUD.cpp


image

중단점을 사용해 InitOverlay 함수에서 입력 매개변수 PC가 어떤 값을 가지고 있는지 가지고 있는지 알 수 있다!



3. Broadcasting


WidgetWidget Controller 형태의 단방향 종속성을 유지하려면 컨트롤러가 초기 값을 브로드캐스트해야 한다. 컨트롤러가 초기값을 브로드캐스트하기 위해 BroadcastInitialValues 함수를 사용한다.


오버레이 위젯 컨트롤러에서 속성값을 브로드캐스트해야 하므로, UOverlayWidgetController 클래스에 델리게이트를 선언한다. 블루프린트에서도 접근이 가능하도록 멀티캐스트 델리게이트를 선언하자.


구현부에 BroadcastInitialValues 함수를 오버라이드해 초기값을 지정한다. 속성값은 AuraAttributeSet를 캐스트해 Health와 MaxHealth를 가져올 수 있다.


cast vs CastChecked

CastChecked는 더 효율적인 캐스트 방식이나, 안전성이 떨어져 캐스트가 확실히 성공할 경우 사용한다.

브로드캐스트로 초기값을 호출하면 NULL값이 나오지 않으므로 CastChecked로 속성값을 호출할 수 있다.

1
2
3
  // CastChecked
  ptrA = Cast< type >( ptrB ); 
  check( ptrA != nullptr );

OverlayWidgetController.cpp

위젯 컨트롤러가 바인딩 된 이후에 브로드캐스트를 수행해야 하므로, HUD 클래스의 SetWidgetController 함수 이후에 브로드캐스트 함수를 호출하는 것이 적합하다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void AAuraHUD::InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS)
{
	// 위젯 및 위젯 컨트롤러 생성 
	// 위젯 컨트롤러 매개변수를 전달하는 GetOverlayWidgetController 함수 호출
...

	// 위젯에 위젯 컨트롤러 설정
	OverlayWidget->SetWidgetController(WidgetController);
	
	// 위젯 컨트롤러가 바인딩 된 이후에 브로드캐스트를 수행해야 함!
	WidgetController->BroadcastInitialValues();

	Widget->AddToViewport();
}

BP_HUD나 HP/Mana progressbar에 위젯 컨트롤러를 바인딩하려면 원시cpp 클래스를 바인딩하기보단 BP로 확장한 후 바인딩하자!

image

위젯 컨트롤러가 BP_OverlayWidgetController로 형변환되었다.


4. Listening & Callback


속성값이 변할 때마다 바인딩하는 함수를 만들 수 있다. BindCallbacksToDependencies 함수를 만들어 바인딩 함수를 작성하면 Health값이 변경될 때마다 브로드캐스트하고 적절한 함수가 호출된다.

GetGameplayAttributeValueChangeDelegate 함수를 통해 오브젝트를 바인딩하고 함수를 호출한다.


1
2
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
		AuraAttributeSet->GetHealthAttribute()).AddUObject(this, &UOverlayWidgetController::MaxHealthChanged);
  • AuraAttributeSet 클래스의 Health값이 변하면 호출
  • AddUObject: 콜백함수가 멤버 함수일 경우 사용(바인딩)
    • 바인딩할 오브젝트
    • 호출할 함수


HUD 클래스의 위젯 컨트롤러 설정이 끝나고 컨트롤러가 반환되기 직전, 위젯 컨트롤러의 콜백 함수를 호출해 속성값이 변할 때마다 바인딩되도록 한다.

변경 사항이나 콜백을 바인딩하는 변경점이 있으면 해당 내용이 브로드캐스트된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UOverlayWidgetController* AAuraHUD::GetOverlayWidgetController(const FWidgetControllerParams& WCParams)
{
	if(OverlayWidgetController == nullptr)
	{
		/** 컨트롤러 설정 */
        ...
		OverlayWidgetController->BindCallbacksToDependencies();

		return OverlayWidgetController;
	}
	...
}

void AAuraHUD::InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS)
{
	...
	
	// 위젯에 위젯 컨트롤러 설정
	OverlayWidget->SetWidgetController(WidgetController);
	...
}

Leave a comment