上一节里面,我们实现了GE被应用时的事件委托,在GE被应用到目标身上时,会触发GE身上设置的Tag,然后通过Tag在设置的表格内寻找表格数据,并添加到窗口显示。
下面,为了防止OverlayWidgetController里面的回调太臃肿,这一节我们将它的代码优化一下。
首先看一下现在的OverlayWidgetController的代码。
OverlayWidgetController.h
cpp
// 版权归暮志未晚所有。
#pragma once
#include "CoreMinimal.h"
#include "UI/WidgetController/MyWidgetController.h"
#include "OverlayWidgetController.generated.h"
class UMyUserWidget;
USTRUCT(BlueprintType)
struct FUIWidgetRow : public FTableRowBase
{
GENERATED_BODY();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FGameplayTag MessageTag = FGameplayTag();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FText Message = FText();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<class UMyUserWidget> MessageWidget;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
UTexture2D* Image = nullptr;
};
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);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMessageWidgetRowSignature,FUIWidgetRow, Row);
/**
* 屏幕覆盖用户控件控制器层基类,继承与用户控件控制器
*/
UCLASS(BlueprintType, Blueprintable)
class AURA_API UOverlayWidgetController : public UMyWidgetController
{
GENERATED_BODY()
public:
virtual void BroadcastInitialValues() override;
virtual void BindCallbacksToDependencies() override;
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;
UPROPERTY(BlueprintAssignable, Category="GAS|Messages")
FMessageWidgetRowSignature MessageWidgetRowDelegate;
protected:
//EditDefaultsOnly 说明此属性可以通过属性窗口编辑,但只能在原型上进行。
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Widget Data")
TObjectPtr<UDataTable> MessageWidgetDataTable;
void HealthChanged(const FOnAttributeChangeData& Data) const;
void MaxHealthChanged(const FOnAttributeChangeData& Data) const;
void ManaChanged(const FOnAttributeChangeData& Data) const;
void MaxManaChanged(const FOnAttributeChangeData& Data) const;
//根据传入的表格和Tag返回查找到的数据,表格类型不确定,所以使用T来表示,在使用此函数时,需要指定对应类型
template<typename T>
T* GetDataTableRowByTag(UDataTable* DataTable, const FGameplayTag& Tag);
};
template <typename T>
T* UOverlayWidgetController::GetDataTableRowByTag(UDataTable* DataTable, const FGameplayTag& Tag)
{
T* Row = DataTable->FindRow<T>(Tag.GetTagName(), TEXT(""));
return Row;
}
OverlayWidgetController.cpp
cpp
// 版权归暮志未晚所有。
#include "UI/WidgetController/OverlayWidgetController.h"
#include "AbilitySystem/AbilitySystemComponentBase.h"
#include "AbilitySystem/AttributeSetBase.h"
void UOverlayWidgetController::BroadcastInitialValues()
{
const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);
OnHealthChanged.Broadcast(AttributeSetBase->GetHealth());
OnMaxHealthChanged.Broadcast(AttributeSetBase->GetMaxHealth());
OnManaChanged.Broadcast(AttributeSetBase->GetMana());
OnMaxManaChanged.Broadcast(AttributeSetBase->GetMaxMana());
}
void UOverlayWidgetController::BindCallbacksToDependencies()
{
const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
AttributeSetBase->GetHealthAttribute()).AddUObject(this, &UOverlayWidgetController::HealthChanged);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
AttributeSetBase->GetMaxHealthAttribute()).AddUObject(this, &UOverlayWidgetController::MaxHealthChanged);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
AttributeSetBase->GetManaAttribute()).AddUObject(this, &UOverlayWidgetController::ManaChanged);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
AttributeSetBase->GetMaxManaAttribute()).AddUObject(this, &UOverlayWidgetController::MaxManaChanged);
//AddLambda 绑定匿名函数
Cast<UAbilitySystemComponentBase>(AbilitySystemComponent)->EffectAssetTags.AddLambda(
[this](const FGameplayTagContainer& AssetTags) //中括号添加this是为了保证内部能够获取类的对象
{
for(const FGameplayTag& Tag : AssetTags)
{
//对标签进行检测,如果不是信息标签,将无法进行广播
FGameplayTag MessageTag = FGameplayTag::RequestGameplayTag(FName("Message"));
// "A.1".MatchesTag("A") will return True, "A".MatchesTag("A.1") will return False
if(Tag.MatchesTag(MessageTag))
{
FUIWidgetRow* Row = GetDataTableRowByTag<FUIWidgetRow>(MessageWidgetDataTable, Tag);
MessageWidgetRowDelegate.Broadcast(*Row); //前面加*取消指针引用
}
//将tag广播给Widget Controller 测试代码
// const FString Msg = FString::Printf(TEXT("GE Tag in Widget Controller: %s"), *Tag.ToString()); //获取Asset Tag
// GEngine->AddOnScreenDebugMessage(-1, 8.f, FColor::Cyan, Msg); //打印到屏幕上 -1 不会被覆盖
}
}
);
}
void UOverlayWidgetController::HealthChanged(const FOnAttributeChangeData& Data) const
{
OnHealthChanged.Broadcast(Data.NewValue);
}
void UOverlayWidgetController::MaxHealthChanged(const FOnAttributeChangeData& Data) const
{
OnMaxHealthChanged.Broadcast(Data.NewValue);
}
void UOverlayWidgetController::ManaChanged(const FOnAttributeChangeData& Data) const
{
OnManaChanged.Broadcast(Data.NewValue);
}
void UOverlayWidgetController::MaxManaChanged(const FOnAttributeChangeData& Data) const
{
OnMaxManaChanged.Broadcast(Data.NewValue);
}
优化宏
以上为现在的代码,我们能够优化的是,将宏的设置这一块
cpp
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);
我们可以使用一个宏作为代替,毕竟它们都是返回一个值,我们可以修改只使用一个类型
cpp
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAttributeChangedSignature, float, NewAttribute);
然后将委托的参数都替换成"FOnAttributeChangedSignature"即可。
cpp
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnMaxHealthChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnManaChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnMaxManaChanged;
使用匿名函数,代替命名函数
现在,我们每个触发委托是先通过定义了一个函数,然后将函数添加到委托里面触发,并在函数里面对属性变化的委托进行广播的。这个函数我们也可以修改成匿名了,这样可以减少很多代码量。
以下为一个委托的当前代码量:
cpp
void HealthChanged(const FOnAttributeChangeData& Data) const;
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
AttributeSetBase->GetHealthAttribute()).AddUObject(this, &UOverlayWidgetController::HealthChanged);
void UOverlayWidgetController::HealthChanged(const FOnAttributeChangeData& Data) const
{
OnHealthChanged.Broadcast(Data.NewValue);
}
我们可以将这些内容优化成一个匿名函数的方式,以下为优化后的方式,而且位置放到了一块
cpp
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
AttributeSetBase->GetHealthAttribute()).AddLambda(
[this](const FOnAttributeChangeData& Data)
{
OnHealthChanged.Broadcast(Data.NewValue);
}
);
并且我们可以直接写成一行,这样,更方便查看。
优化结果
接下来我们看一下优化完成后的结果
OverlayWidgetController.h
cpp
// 版权归暮志未晚所有。
#pragma once
#include "CoreMinimal.h"
#include "UI/WidgetController/MyWidgetController.h"
#include "OverlayWidgetController.generated.h"
class UMyUserWidget;
USTRUCT(BlueprintType)
struct FUIWidgetRow : public FTableRowBase
{
GENERATED_BODY();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FGameplayTag MessageTag = FGameplayTag();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FText Message = FText();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<class UMyUserWidget> MessageWidget;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
UTexture2D* Image = nullptr;
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAttributeChangedSignature, float, NewAttribute);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMessageWidgetRowSignature,FUIWidgetRow, Row);
/**
* 屏幕覆盖用户控件控制器层基类,继承与用户控件控制器
*/
UCLASS(BlueprintType, Blueprintable)
class AURA_API UOverlayWidgetController : public UMyWidgetController
{
GENERATED_BODY()
public:
virtual void BroadcastInitialValues() override;
virtual void BindCallbacksToDependencies() override;
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnMaxHealthChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnManaChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
FOnAttributeChangedSignature OnMaxManaChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Messages")
FMessageWidgetRowSignature MessageWidgetRowDelegate;
protected:
//EditDefaultsOnly 说明此属性可以通过属性窗口编辑,但只能在原型上进行。
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Widget Data")
TObjectPtr<UDataTable> MessageWidgetDataTable;
//根据传入的表格和Tag返回查找到的数据,表格类型不确定,所以使用T来表示,在使用此函数时,需要指定对应类型
template<typename T>
T* GetDataTableRowByTag(UDataTable* DataTable, const FGameplayTag& Tag);
};
template <typename T>
T* UOverlayWidgetController::GetDataTableRowByTag(UDataTable* DataTable, const FGameplayTag& Tag)
{
T* Row = DataTable->FindRow<T>(Tag.GetTagName(), TEXT(""));
return Row;
}
OverlayWidgetController.cpp
cpp
// 版权归暮志未晚所有。
#include "UI/WidgetController/OverlayWidgetController.h"
#include "AbilitySystem/AbilitySystemComponentBase.h"
#include "AbilitySystem/AttributeSetBase.h"
void UOverlayWidgetController::BroadcastInitialValues()
{
const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);
OnHealthChanged.Broadcast(AttributeSetBase->GetHealth());
OnMaxHealthChanged.Broadcast(AttributeSetBase->GetMaxHealth());
OnManaChanged.Broadcast(AttributeSetBase->GetMana());
OnMaxManaChanged.Broadcast(AttributeSetBase->GetMaxMana());
}
void UOverlayWidgetController::BindCallbacksToDependencies()
{
const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetHealthAttribute()).AddLambda([this](const FOnAttributeChangeData& Data){OnHealthChanged.Broadcast(Data.NewValue);});
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetMaxHealthAttribute()).AddLambda([this](const FOnAttributeChangeData& Data){OnMaxHealthChanged.Broadcast(Data.NewValue);});
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetManaAttribute()).AddLambda([this](const FOnAttributeChangeData& Data){OnManaChanged.Broadcast(Data.NewValue);});
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetMaxManaAttribute()).AddLambda([this](const FOnAttributeChangeData& Data){OnMaxManaChanged.Broadcast(Data.NewValue);});
//AddLambda 绑定匿名函数
Cast<UAbilitySystemComponentBase>(AbilitySystemComponent)->EffectAssetTags.AddLambda(
[this](const FGameplayTagContainer& AssetTags) //中括号添加this是为了保证内部能够获取类的对象
{
for(const FGameplayTag& Tag : AssetTags)
{
//对标签进行检测,如果不是信息标签,将无法进行广播
FGameplayTag MessageTag = FGameplayTag::RequestGameplayTag(FName("Message"));
// "A.1".MatchesTag("A") will return True, "A".MatchesTag("A.1") will return False
if(Tag.MatchesTag(MessageTag))
{
FUIWidgetRow* Row = GetDataTableRowByTag<FUIWidgetRow>(MessageWidgetDataTable, Tag);
MessageWidgetRowDelegate.Broadcast(*Row); //前面加*取消指针引用
}
//将tag广播给Widget Controller 测试代码
// const FString Msg = FString::Printf(TEXT("GE Tag in Widget Controller: %s"), *Tag.ToString()); //获取Asset Tag
// GEngine->AddOnScreenDebugMessage(-1, 8.f, FColor::Cyan, Msg); //打印到屏幕上 -1 不会被覆盖
}
}
);
}
优化最大的还是cpp文件,清晰了不少,而且代码还更加的精简。
运行调试
我们在运行起来UE,会提示有两个蓝图文件出现问题,它们的问题相同都是因为修改代码后,委托的类型变化造成的。
进入后编译会提示错误,我们找到错误的地方查看
右键点击刷新节点
刷新以后,重新链接新的属性即可。