Udemy Unreal - Gameplay Ability System - Top Down RPG (Section 4)

๊น€์ข…๋ฏผยท2025๋…„ 4์›” 1์ผ

Udemy Unreal - GAS

๋ชฉ๋ก ๋ณด๊ธฐ
4/7

๐Ÿ“– ์œ ๋ฐ๋ฏธ GAS ๊ฐ•์˜ ์ค‘ ๊ธฐ๋กํ•ด๋‘๊ณ  ์‹ถ์€ ๋‚ด์šฉ์„ ์ ์–ด๋‘” ํฌ์ŠคํŠธ

Section 4 : UI ๊ตฌํ˜„ (MVC ํŒจํ„ด)


MVC ํŒจํ„ด

์ฐธ๊ณ ์˜์ƒ-๋ฐฐ๋ฏผ ํ…Œ์ฝ”ํ†ก

View : ๊ฐ WBP
WidgetController : WidgetController ํด๋ž˜์Šค
Model : Data๋“ค

View -> Controller -> Model ์˜ ์ผ๋ฐฉํ–ฅ์  ์ข…์†์„ฑ์„ ์ง€๋‹ˆ๋„๋ก ํ•œ๋‹ค.
(Model์€ Controller์˜ ์ƒ์„ธ๋ฅผ ๋ชจ๋ฅด๊ณ , Controller๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ Widget์œผ๋กœ๋ถ€ํ„ฐ ๋…๋ฆฝ์ )


View

UAuraUserWidget

public:
 	// ์œ„์ ฏ ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์—์„œ ์ปจํŠธ๋กค๋Ÿฌ ์…‹ํ•  ์ˆ˜ ์žˆ๋„๋ก
 	UFUNCTION(BlueprintCallable)
 	void SetWidgetController(UObject* InWidgetController);
 	
 	// ์ผ๋ฐฉํ–ฅ ์ข…์†์„ฑ์„ ์œ„ํ•ด ์œ„์ ฏ์ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํฌ์ธํ„ฐ๋กœ ๋“ค๊ณ  ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์œ„์ ฏ ์•ˆ ๋“ค๊ณ  ์žˆ์Œ
 	UPROPERTY(BlueprintReadOnly)
 	TObjectPtr<UObject> WidgetController;
 
 protected:
 	// ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์…‹ ๋๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ์œ„ํ•œ ํ•จ์ˆ˜๋กœ ์ผ์ข…์˜ BeginPlay ์—ญํ•  ์ˆ˜ํ–‰
 	UFUNCTION(BlueprintImplementableEvent)
 	void WidgetControllerSet();
void UAuraUserWidget::SetWidgetController(UObject* InWidgetController)
{
	WidgetController = InWidgetController;
	// ๋ธ”๋ฃจํ”„๋ฆฐํŠธ ์ด๋ฒคํŠธ ํ˜ธ์ถœ
	WidgetControllerSet();
}

๋ธ”๋ฃจํ”„๋ฆฐํŠธ์—์„œ ์‚ฌ์šฉ ์˜ˆ์‹œ


Controller

UAuraWidgetController

UObject ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•˜๋‹ค.

public:
	// FWidgetControllerParams๋Š” 4๊ฐœ์˜ ๋ฉค๋ฒ„๋ฅผ ๋ฌถ์–ด๋†“์€ ๊ตฌ์กฐ์ฒด์ž„
 	UFUNCTION(BlueprintCallable)
 	void SetWidgetControllerParams(const FWidgetControllerParams& WCParams);
 
 	
 protected:
 	// ์ž์‹ ์ปจํŠธ๋กค๋Ÿฌ๋“ค์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก protected
 	// Model์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ํฌ์ธํ„ฐ๋“ค (์ผ๋ฐฉํ–ฅ ์ข…์†์„ฑ ์œ ์ง€ View -> Controller -> Model) 
 	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
 	TObjectPtr<APlayerController> PlayerController;
 
 	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
 	TObjectPtr<APlayerState> PlayerState;
 
 	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
 	TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
 
 	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
 	TObjectPtr<UAttributeSet> AttributeSet;

HUD

์ปจํŠธ๋กค๋Ÿฌ ์ƒ์„ฑ๊ณผ ๋ฉค๋ฒ„ ํ• ๋‹น์˜ ํƒ€์ด๋ฐ์„ ์žก๊ธฐ์œ„ํ•ด HUD ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•œ๋‹ค.
๊ฒŒ์ž„๋ชจ๋“œ์—์„œ HUD ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•˜๊ณ  ์‚ฌ์šฉํ•œ๋‹ค.

public:
 
 	UPROPERTY()
 	TObjectPtr<UAuraUserWidget> OverlayWidget;
 
 	// Controller๋„ ๊ฐ™์ด ๋งŒ๋“ค๊ธฐ (Getter์ง€๋งŒ ์—†๋‹ค๋ฉด ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ  ์ด๋ฏธ ์žˆ๋‹ค๋ฉด ์žˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋ฆฌํ„ด)
 	UOverlayWidgetController* GetOverlayWidgetController(const FWidgetControllerParams& WCParams);
 
 	// Overlay ๊ตฌ์„ฑ์„ ์œ„ํ•œ ์œ„์ ฏ๊ณผ ์ปจํŠธ๋กค๋Ÿฌ ์ƒ์„ฑ
 	void InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS);
 
 	
 private:
 
 	UPROPERTY(EditAnywhere)
 	TSubclassOf<UAuraUserWidget> OverlayWidgetClass;
 
 	// ์ปจํŠธ๋กค๋Ÿฌ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค์™€ ๋งŒ๋“  ๋’ค์˜ ํฌ์ธํ„ฐ
 	UPROPERTY(EditAnywhere)
 	TSubclassOf<UOverlayWidgetController> OverlayWidgetControllerClass;
 	UPROPERTY()
 	TObjectPtr<UOverlayWidgetController> OverlayWidgetController;

InitOverlay() ํ•จ์ˆ˜์—์„œ ์œ„์ ฏ ์ƒ์„ฑ๊ณผ ์ปจํŠธ๋กค๋Ÿฌ ํ• ๋‹น


 UOverlayWidgetController* AAuraHUD::GetOverlayWidgetController(const FWidgetControllerParams& WCParams)
 {
 	if (OverlayWidgetController == nullptr)
 	{
 		OverlayWidgetController = NewObject<UOverlayWidgetController>(this,OverlayWidgetControllerClass);
 		OverlayWidgetController->SetWidgetControllerParams(WCParams);
 	}
 	return OverlayWidgetController;
 }
 
 void AAuraHUD::InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS)
 {
 	// ๋‹จ์ˆœ check - crash ๋ณด๋‹ค ์ƒ์„ธํ•˜๊ฒŒ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ
 	checkf(OverlayWidgetClass, TEXT("OverlaywidgetClass Uninitialized, Check BP_AuraHUD"));
 	checkf(OverlayWidgetControllerClass, TEXT("OverlayWidgetControllerClass Uninitialized, Check BP_AuraHUD"));
 
 	
 	UUserWidget* Widget = CreateWidget<UUserWidget>(GetWorld(),OverlayWidgetClass);
 	OverlayWidget = Cast<UAuraUserWidget>(Widget);
 	const FWidgetControllerParams WCParams(PC,PS,ASC,AS);
 	OverlayWidgetController = GetOverlayWidgetController(WCParams);
 	OverlayWidget->SetWidgetController(OverlayWidgetController);
 	
 	Widget->AddToViewport();
 	
 }

InitOverlay()์˜ ํ˜ธ์ถœ์€ AAuraCharacter::InitAbilityActorInfo()์—์„œ

  • Controller ์ƒ์„ฑ์— ํ•„์š”ํ•œ 4๊ฐ€์ง€ ๋ฉค๋ฒ„๊ฐ€ ๋ชจ๋‘ ์ƒ์„ฑ๋œ ์ดํ›„์— ํ•ด์•ผํ•˜๋ฏ€๋กœ ์ด ๋ถ€๋ถ„์ด ์ ์ ˆํ•œ ์‹œ์ ์ด๋‹ค. (์ด๋ฏธ ํฌ์ธํ„ฐ๋ฅผ ๋ฐ›์•„๋†”์„œ ์ „๋‹ฌํ•˜๊ธฐ๋„ ํŽธํ•จ)
  • PlayerController๋ฅผ ํ†ตํ•ด HUD์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
void AAuraCharacter::InitAbilityActorInfo()
{
 	AbilitySystemComponent = AuraPlayerState->GetAbilitySystemComponent();
 	AbilitySystemComponent->InitAbilityActorInfo(AuraPlayerState, this);
 	AttributeSet = AuraPlayerState->GetAttributeSet();
 
 	// InitOverlay
 	// ๋ฉ€ํ‹ฐํ”Œ๋ ˆ์ด์–ด์˜ ๊ฒฝ์šฐ ํ”Œ๋ ˆ์ด์–ด๋ธ” ์บ๋ฆญํ„ฐ๊ฐ€ ํ•ญ์ƒ Playercontroller๋ฅผ ๋“ค๊ณ ์žˆ์ง€๋Š” ์•Š๊ธฐ๋•Œ๋ฌธ์— ValidCheck์„ ํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋ฉด
 	// ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ปจํŠธ๋กค ์ค‘์ธ ํ”Œ๋ ˆ์ด์–ด ์บ๋ฆญํ„ฐ์—์„œ ASSERT๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ๋•Œ๋ฌธ์— if()๋กœ valid ํ™•์ธ๋œ ๊ฒฝ์šฐ๋งŒ Initํ•ด์ฃผ๋ฉด ๋จ
 	if (APlayerController* PlayerController = GetController<APlayerController>())
 	{
 	// check(PlayerController) X , ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ HUD๋„ 1 ํ”Œ๋ ˆ์ด์–ดํ•œํ…Œ๋งŒ ์žˆ์œผ๋‹ˆ๊นŒ if() ์ฒดํฌ๋งŒ ํ•˜๊ธฐ
 		if (AAuraHUD* AuraHUD = PlayerController->GetHUD<AAuraHUD>())
 		{
 			AuraHUD->InitOverlay(PlayerController,AuraPlayerState,AbilitySystemComponent,AttributeSet);
 		}
 	}
}

๋ฐ”์ธ๋”ฉ๊ณผ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ

๋ธ๋ฆฌ๊ฒŒ์ดํŠธ

์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ˆ˜ํ–‰

OverlayWidgetController.h

 // Dynamic : BP(ํŠนํžˆ WBP)์— ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ฏ€๋กœ
 // Multicast : ์—ฌ๋Ÿฌ Widget์— ์—ฐ๊ฒฐํ•  ์˜ˆ์ •์ด๋ฏ€๋กœ
 DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangedSignature,float,NewHealth);
 
 // BluprintAssignable : Multicast์— ์‚ฌ์šฉ, ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์—์„œ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๊ฐ€ ๋…ธ์ถœ๋˜๋„๋ก
 	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
 	FOnHealthChangedSignature OnHealthChanged;
    
 // ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋“ค ๋งŒ๋“ค๊ธฐ
 	// ASC์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์–‘์‹ ๋งž์ถ”๊ธฐ (const FOnAttributeChangeData&) 
 	void HealthChanged(const FOnAttributeChangeData& Data) const;

OverlayWidgetController.cpp

void UOverlayWidgetController::BroadcastInitialValues()
 {
 	// AttributeSet ๋ฉค๋ฒ„๊ฐ€ ํ™•์‹คํžˆ ํ• ๋‹น๋œ ์ดํ›„์— ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…๋œ๋‹ค๋Š” ๊ฒƒ์„ ์ „์ œ๋กœ ํ•˜๋ฏ€๋กœ CastChecked ์‚ฌ์šฉ
 	UAuraAttributeSet* AuraAttributeSet = CastChecked<UAuraAttributeSet>(AttributeSet);
 	OnHealthChanged.Broadcast(AuraAttributeSet->GetHealth());
 }
 
 void UOverlayWidgetController::BindCallbacksToDependencies()
 {
 	UAuraAttributeSet* AuraAttributeSet = CastChecked<UAuraAttributeSet>(AttributeSet);
 
 	// ASC์˜ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๋ฐ›์•„์˜ค๊ธฐ, Dynamic ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ AddUObject๋กœ ๋ฐ”์ธ๋”ฉ
 	// ๋ฐ”์ธ๋”ฉํ•  ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๊ตฌ์กฐ์ฒด๋ฅผ ์ธ์ž๋กœ ๋„ฃ๊ธฐ
 	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
    AuraAttributeSet->GetHealthAttribute()) // ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๋ถˆ๋Ÿฌ์˜ค๊ณ 
 	.AddUObject(this,&UOverlayWidgetController::HealthChanged); // ๋ฐ”์ธ๋”ฉ (์˜ค๋ธŒ์ ํŠธ, ์ฝœ๋ฐฑํ•จ์ˆ˜)
 }
 
 void UOverlayWidgetController::HealthChanged(const FOnAttributeChangeData& Data) const
 {
 	OnHealthChanged.Broadcast(Data.NewValue);
 }

๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ํ• ๋‹น ์˜ˆ์‹œ


์ปค๋ฐ‹

https://github.com/jongmin7759/Unreal_GAS/commit/f8daafac54a063629bf40d2d0af01b7b7dbc74b3
https://github.com/jongmin7759/Unreal_GAS/commit/db3f63fb9566ba3d6666d7cecd2644ef1f3eecc9

profile
์ •๋ฆฌ์™€ ๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€