虚幻引擎5 GAS开发俯视角RPG游戏 #5-8:倾听属性变化
摘要:本文展示了一个在UE5中实现属性监听回调的系统设计。首先在WidgetController基类中定义虚函数BindCallbacksToDependencies(),然后在OverlayWidgetController子类中实现该函数,监听生命值和法力值属性的变化。当属性变化时触发对应的回调函数,通过广播机制通知UI更新。属性集初始值在CC_AttributeSet中设置,而回调绑定函数在HUD中初始化WidgetController后调用。最终实现了属性变化时UI自动更新的效果。
1.在类Source/CC_Aura/Public/UI/WidgetController/CC_WidgetController.h里写父函数:
//依赖的绑定回调函数virtual void BindCallbacksToDependencies();
2.在子类实现:
Source/CC_Aura/Public/UI/WidgetController/CC_OverlayWidgetController.h:
//依赖的绑定回调函数virtual void BindCallbacksToDependencies() override;
Source/CC_Aura/Private/UI/WidgetController/CC_OverlayWidgetController.cpp:
void UCC_OverlayWidgetController::BindCallbacksToDependencies()
{if (CC_AttributeSet == nullptr){CC_AttributeSet = Cast<UCC_AttributeSet>(AttributeSet);}//监听属性变化AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetHealthAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::HealthChanged);AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetMaxHealthAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::MaxHealthChanged);AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetManaAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::ManaChanged);AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetMaxManaAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::MaxManaChanged);
}
回调函数:
void UCC_OverlayWidgetController::HealthChanged(const FOnAttributeChangeData& Data) const
{OnHealthChanged.Broadcast(Data.NewValue);
}void UCC_OverlayWidgetController::MaxHealthChanged(const FOnAttributeChangeData& Data) const
{OnMaxHealthChanged.Broadcast(Data.NewValue);
}void UCC_OverlayWidgetController::ManaChanged(const FOnAttributeChangeData& Data) const
{OnManaChanged.Broadcast(Data.NewValue);
}void UCC_OverlayWidgetController::MaxManaChanged(const FOnAttributeChangeData& Data) const
{OnMaxManaChanged.Broadcast(Data.NewValue);
}
这段代码展示了UE5中GAS(游戏能力系统)的UI属性绑定机制,主要功能是通过委托系统实现属性变化时UI的自动更新。以下是关键点分析:
属性监听机制:
- 使用
GetGameplayAttributeValueChangeDelegate
获取属性变化的委托 - 通过
AddUObject
将成员函数绑定为回调
委托广播实现:
- 定义了多个动态多播委托类型(如
FOnHealthChangedSignature
) - 属性变化时通过
Broadcast
方法通知所有监听者
数据传递方式:
- 使用
FOnAttributeChangeData
结构体传递新旧属性值 - 通过
NewValue
获取更新后的属性数值
控制器架构:
- 属于WidgetController模式
- 负责连接数据层(AttributeSet)和表现层(Widget)
这种实现方式在RPG游戏中常用于实时更新血条、魔法值等UI元素,通过委托机制实现数据与UI的解耦。
3.将属性集初始值改变,到蓝图里测试:
Source/CC_Aura/Private/AbilitySystem/CC_AttributeSet.cpp:
UCC_AttributeSet::UCC_AttributeSet()
{InitHealth(45.f);InitMaxHealth(100.f);InitMana(50.f);InitMaxMana(100.f);
}
4.但,我们到哪里去调用函数BindCallbacksToDependencies()呢?什么时候调用?
答案:
我们在HUD中进行调用
当小部件控制器设置好参数后,就可以绑定监听数据的函数了:
Source/CC_Aura/Private/UI/HUD/CC_HUD.cpp:
效果:
吃药前:
吃药后:
完美!!!!!!!
源代码:
Source/CC_Aura/Public/UI/WidgetController/CC_OverlayWidgetController.h:
// 版权归陈超所有#pragma once#include "CoreMinimal.h"
#include "UI/WidgetController/CC_WidgetController.h"
#include "CC_OverlayWidgetController.generated.h"struct FOnAttributeChangeData;
class UCC_AttributeSet;
//添加动态多播委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangedSignature, float, NewHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMaxHealthChangedSignature, float, NewMaxHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnManaChangedSignature, float, NewMana);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMaxManaChangedSignature, float, NewMaxMana);/*** */
UCLASS(Blueprintable)
class CC_AURA_API UCC_OverlayWidgetController : public UCC_WidgetController
{GENERATED_BODY()
public://广播初始值virtual void BroadcastInitialValues() override;//依赖的绑定回调函数virtual void BindCallbacksToDependencies() override;
protected://动态多播委托UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")FOnHealthChangedSignature OnHealthChanged;UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")FOnMaxHealthChangedSignature OnMaxHealthChanged;UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")FOnManaChangedSignature OnManaChanged;UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")FOnMaxManaChangedSignature OnMaxManaChanged;private:UPROPERTY()TObjectPtr<UCC_AttributeSet> CC_AttributeSet;//属性变化的回调函数void HealthChanged(const FOnAttributeChangeData& Data) const;void MaxHealthChanged(const FOnAttributeChangeData& Data) const;void ManaChanged(const FOnAttributeChangeData& Data) const;void MaxManaChanged(const FOnAttributeChangeData& Data) const;};
Source/CC_Aura/Private/UI/WidgetController/CC_OverlayWidgetController.cpp:
// 版权归陈超所有#include "UI/WidgetController/CC_OverlayWidgetController.h"#include "AbilitySystem/CC_AttributeSet.h"void UCC_OverlayWidgetController::BroadcastInitialValues()
{if (CC_AttributeSet == nullptr){CC_AttributeSet = Cast<UCC_AttributeSet>(AttributeSet);}//广播属性变化OnHealthChanged.Broadcast(CC_AttributeSet->GetHealth());OnMaxHealthChanged.Broadcast(CC_AttributeSet->GetMaxHealth());OnManaChanged.Broadcast(CC_AttributeSet->GetMana());OnMaxManaChanged.Broadcast(CC_AttributeSet->GetMaxMana());
}void UCC_OverlayWidgetController::BindCallbacksToDependencies()
{if (CC_AttributeSet == nullptr){CC_AttributeSet = Cast<UCC_AttributeSet>(AttributeSet);}//监听属性变化AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetHealthAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::HealthChanged);AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetMaxHealthAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::MaxHealthChanged);AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetManaAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::ManaChanged);AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(CC_AttributeSet->GetMaxManaAttribute()) .AddUObject(this, &UCC_OverlayWidgetController::MaxManaChanged);
}void UCC_OverlayWidgetController::HealthChanged(const FOnAttributeChangeData& Data) const
{OnHealthChanged.Broadcast(Data.NewValue);
}void UCC_OverlayWidgetController::MaxHealthChanged(const FOnAttributeChangeData& Data) const
{OnMaxHealthChanged.Broadcast(Data.NewValue);
}void UCC_OverlayWidgetController::ManaChanged(const FOnAttributeChangeData& Data) const
{OnManaChanged.Broadcast(Data.NewValue);
}void UCC_OverlayWidgetController::MaxManaChanged(const FOnAttributeChangeData& Data) const
{OnMaxManaChanged.Broadcast(Data.NewValue);
}
Source/CC_Aura/Public/UI/HUD/CC_HUD.h:
// 版权归陈超所有#pragma once#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "CC_HUD.generated.h"class UAttributeSet;
class UAbilitySystemComponent;
struct FWidgetControllerParams;
class UCC_OverlayWidgetController;
class UCC_UserWidget;
/*** */
UCLASS()
class CC_AURA_API ACC_HUD : public AHUD
{GENERATED_BODY()public:UPROPERTY(EditAnywhere, BlueprintReadWrite)TObjectPtr<UCC_UserWidget> OverlayWidget; //覆盖层小部件UCC_OverlayWidgetController* GetOverlayWidgetController(/*const FWidgetControllerParams& FWCParams*/);void InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS);private:UPROPERTY(EditAnywhere)TSubclassOf<UCC_UserWidget> OverlayWidgetClass; //覆盖层小部件类UPROPERTY()TObjectPtr<UCC_OverlayWidgetController> OverlayWidgetController; //覆盖层小部件控制器//覆盖层小部件的类、子类UPROPERTY(EditAnywhere)TSubclassOf<UCC_OverlayWidgetController> OverlayWidgetControllerClass;
};
Source/CC_Aura/Private/UI/HUD/CC_HUD.cpp:
// 版权归陈超所有#include "UI/HUD/CC_HUD.h"
#include "UI/Widget/CC_UserWidget.h"
#include "UI/WidgetController/CC_OverlayWidgetController.h"UCC_OverlayWidgetController* ACC_HUD::GetOverlayWidgetController(/*const FWidgetControllerParams& FWCParams*/)
{if (OverlayWidgetController == nullptr) {//创建覆盖层小部件控制器OverlayWidgetController = NewObject<UCC_OverlayWidgetController>(this, OverlayWidgetControllerClass);// OverlayWidgetController->SetWidgetControllerParams(FWCParams);}return OverlayWidgetController;
}void ACC_HUD::InitOverlay(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS)
{checkf(OverlayWidgetClass,TEXT("OverlayWidgetClass没有设定!"));checkf(OverlayWidgetControllerClass,TEXT("OverlayWidgetControllerClass没有设定!"));//1.创建小部件UUserWidget* Widget = CreateWidget<UUserWidget>(GetWorld(), OverlayWidgetClass);OverlayWidget = Cast<UCC_UserWidget>(Widget);//2.创建小部件控制器const FWidgetControllerParams WidgetControllerParams = FWidgetControllerParams(PC, PS, ASC, AS);//获取覆盖层小部件控制器指针OverlayWidgetController = GetOverlayWidgetController();//将参数赋值给小部件控制器OverlayWidgetController->SetWidgetControllerParams(WidgetControllerParams);//小部件控制器监听属性变化,触发回调OverlayWidgetController->BindCallbacksToDependencies();//3.将小部件控制器添加到小部件OverlayWidget->AddWidgetController(OverlayWidgetController);//4.小部件控制器广播初始值OverlayWidgetController->BroadcastInitialValues();//将覆盖层小部件打印到屏幕OverlayWidget->AddToViewport();
}