文件结构
Source
- Private
- AbilitySystemComponen
- RPGAbilitySystemComponent.cpp
- RPGAttributeSet.cpp
- Character
- PGGameCharacterBase.cpp
- RPGGameEnemy.cpp
- RPGGamePlayerCharacter.cpp
- Game
- RPGGameModeBase.cpp
- Interaction
- EnemyInterface.cpp
- Player
- RPGPlayerController.cpp
- RPGPlayerState.cpp
- Actor
- RPGEffectActor.cpp
- UI
- HUD
- RPGHUD.cpp
- WidgetController
- OverlayWidgetController.cpp
- RPGWidgetController.cpp
- Widgets
- RPGUserWidget.cpp
- HUD
- AbilitySystemComponen
- Public
- AbilitySystemComponent
- RPGAbilitySystemComponent.h
- RPGAttributeSet.h
- Character
- RPGGameCharacterBase.h
- RPGGameEnemy.h
- RPGGamePlayerCharacter.h
- Game
- RPGGameModeBase.h
- Interaction
- EnemyInterface.h
- Player
- RPGPlayerController.h
- RPGPlayerState.h
- Actor
- RPGEffectActor.h
- UI
- HUD
- RPGHUD.h
- WidgetController
- OverlayWidgetController.h
- RPGWidgetController.h
- Widgets
- RPGUserWidget.h
- HUD
- AbilitySystemComponent
文件概述
RPGAbilitySystemComponent
.h文件
// Copyright KimiLiu
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "RPGAbilitySystemComponent.generated.h"
DECLARE_MULTICAST_DELEGATE_OneParam(FEffectAssetTags, const FGameplayTagContainer&/*AssetTags*/);
/**
* Ability System Component 拥有两个Owner: Owner Actor 以及Avatar Actor,这两个Actor有可能相同,有可能不相同
* Owner Actor 指的是实例化组件的Actor
* Avatar Actor 指的是ASC实际上服务的Actor,例如
* - RPGGameEnemy 内的ASC的 Owner Actor 和 Avatar Actor 都是 RPGGameEnemy 这个 Actor 类自身。因为ASC由 RPGGameEnemy 实例
* 化,同时服务于 RPGGameEnemy
* - RPGGamePlayerCharacter 内的ASC的 Owner Actor 是 RPGPlayerState,而 Avatar Actor 是RPGGamePlayerCharacter。因为
* ASC由RPGPlayerState实例化,但是ASC服务于PRGGamePlayerCharacter
*/
UCLASS()
class AURA_API URPGAbilitySystemComponent : public UAbilitySystemComponent
{
GENERATED_BODY()
public:
// ASC初始化完成(设置了实际所有者以及逻辑所有者)
void AbilityActorInfoSet();
//
FEffectAssetTags EffectAssetTags;
protected:
// 当有任何GE在自身上被应用时会触发委托调用该函数
void EffectApplied(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayEffectSpec& GameplayEffectSpec, FActiveGameplayEffectHandle ActiveGameplayEffectHandle);
};
.cpp文件
// Copyright KimiLiu
#include "AbilitySytstem/RPGAbilitySystemComponent.h"
void URPGAbilitySystemComponent::AbilityActorInfoSet()
{
// 绑定委托
OnGameplayEffectAppliedDelegateToSelf.AddUObject(this, &URPGAbilitySystemComponent::EffectApplied);
}
void URPGAbilitySystemComponent::EffectApplied(UAbilitySystemComponent* AbilitySystemComponent,
const FGameplayEffectSpec& GameplayEffectSpec, FActiveGameplayEffectHandle ActiveGameplayEffectHandle)
{
//GEngine->AddOnScreenDebugMessage(1, 5.f, FColor::Red, FString("Effect Applied"));
FGameplayTagContainer TagContainer;
GameplayEffectSpec.GetAllAssetTags(TagContainer);
// 广播
EffectAssetTags.Broadcast(TagContainer);
}
RPGAttributeSet
.h文件
// Copyright KimiLiu
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "AttributeSet.h"
#include "RPGAttributeSet.generated.h"
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
// 逻辑处理数据结构,在这个结构中能够获取目标ASC以及源头ASC,以及目标对象,源头对象,目标控制器,源头控制器
USTRUCT()
struct FEffectProperties
{
GENERATED_BODY()
FEffectProperties(){};
FGameplayEffectContextHandle EffectContextHandle;
UPROPERTY()
UAbilitySystemComponent* SourceASC = nullptr;
UPROPERTY()
AActor* SourceAvatarActor = nullptr;
UPROPERTY()
AController* SourceController = nullptr;
UPROPERTY()
ACharacter* SourceCharacter = nullptr;
UPROPERTY()
UAbilitySystemComponent* TargetASC = nullptr;
UPROPERTY()
AActor* TargetAvatarActor = nullptr;
UPROPERTY()
AController* TargetController = nullptr;
UPROPERTY()
ACharacter* TargetCharacter = nullptr;
};
/**
* AS拥有预测功能(Prediction)能够让多人游戏的客户端在拥有更少的延迟。客户端能够立刻改变自己维护的AS,然后通知服务端,由服务端判定这个更
* 改是否合法,如果不合法,则服务端拒绝更改AS并通知客户端回滚AS
*/
UCLASS()
class AURA_API URPGAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
URPGAttributeSet();
//复制变量时必须重写的函数,用于注册需要复制的变量
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
//在属性被更改前调用,不用来处理逻辑,只用来限制值大小
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
//在属性被更改后调用,用来处理逻辑
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
/**
* 创建AS属性步骤:
* 1. 声明FGameplayAttributeData类型变量
* 2. 用UPROPERTY()宏修饰
* 3. 如果是多人游戏,需要在宏内声明: ReplicatedUsing = OnRep_属性名,同时声明一个UFUNCTION()方法OnRep_属性名()。当服务端的该属性
* 值变化时,OnRep_属性名()将会被调用,我们在这个函数内处理变化事件
* 4. 实现OnRep_属性名()函数,在函数内调用GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, 属性名, 旧属性值)宏,用来保存旧值用于
* 服务端通知客户端进行回滚
* 5. 重写GetLifetimeReplicatedProps()函数,在该函数内注册属性
* 6. 使用ATTRIBUTE_ACCESSORS()宏来初始化value_getter, property_getter, value_setter, initter
*/
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health, Category="Vital Attribute")
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS(URPGAttributeSet, Health);
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth, Category="Vital Attribute")
FGameplayAttributeData MaxHealth;
ATTRIBUTE_ACCESSORS(URPGAttributeSet, MaxHealth);
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Mana, Category="Vital Attribute")
FGameplayAttributeData Mana;
ATTRIBUTE_ACCESSORS(URPGAttributeSet, Mana);
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Mana, Category="Vital Attribute")
FGameplayAttributeData MaxMana;
ATTRIBUTE_ACCESSORS(URPGAttributeSet, MaxMana);
UFUNCTION()
void OnRep_Health(const FGameplayAttributeData& OldHealth) const;
UFUNCTION()
void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const ;
UFUNCTION()
void OnRep_Mana(const FGameplayAttributeData& OldMana) const;
UFUNCTION()
void OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const;
private:
//设置FEffectProperties类数据,Props内有很多信息,参照上面的结构体声明
void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const;
};
.cpp文件
// Copyright KimiLiu
#include "AbilitySytstem/RPGAttributeSet.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "Net/UnrealNetwork.h"
#include "GameplayEffectExtension.h"
#include "GameFramework/Character.h"
URPGAttributeSet::URPGAttributeSet()
{
InitHealth(50.f);
InitMaxHealth(100.f);
InitMana(50.f);
InitMaxMana(100.f);
}
void URPGAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//注册需要复制的属性Health,没有复制条件,不论复制的结果是否等于客户端原有结果,都进行复制调用
DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, Health, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, Mana, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, MaxMana, COND_None, REPNOTIFY_Always);
}
// 此处的Clamp只能更改CurrentValue,不能更改BaseValue,因此还需要在Post函数中做一次Clamp
void URPGAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);
if (Attribute == GetHealthAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
}
if (Attribute == GetMaxHealthAttribute())
{
}
if (Attribute == GetManaAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMana());
}
if (Attribute == GetMaxManaAttribute())
{
}
}
void URPGAttributeSet::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const
{
// Source = causer of the effect, Target = target of the effect (owner of this AS)
// 拿到源头的上下文,上下文内拥有指向ASC的指针
Props.EffectContextHandle = Data.EffectSpec.GetContext();
Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent();
if (IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
{
// 获取逻辑拥有者
Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get();
// 获取控制器
Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get();
// 若ASC组件没有控制器,则从逻辑拥有者那里获取控制器
if (Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr)
{
// Source有可能没有Controller
if (const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
{
Props.SourceAvatarActor = Pawn->GetController();
}
}
// 获取来源角色
if (Props.SourceController)
{
Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn());
}
}
// 拿到目标Actor
if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
{
Props.TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
Props.TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
Props.TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor);
Props.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor);
}
}
// Data 很强大,我们可以从中拿到任何我们想拿到的东西,但是得注意是否是空指针,需要做判断
void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
// Source = causer of the effect, Target = target of the effect (owner of this AS)
FEffectProperties Props;
SetEffectProperties(Data, Props);
// 可以用Prps来取到我们想要的值,处理大部分逻辑
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
GEngine->AddOnScreenDebugMessage(1, 3.f, FColor::Red, FString::Printf(TEXT("Health: %f"), GetHealth()));
SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
}
if (Data.EvaluatedData.Attribute == GetManaAttribute())
{
GEngine->AddOnScreenDebugMessage(1, 3.f, FColor::Blue, FString::Printf(TEXT("Mana: %f"), GetHealth()));
SetMana(FMath::Clamp(GetMana(), 0.f, GetMaxMana()));
}
}
void URPGAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) const
{
// 当Health属性被调用,此函数被调用,传入OldHealth作为旧值,该旧值将会被保存以免服务端通知客户端该属性需要回滚
GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, Health, OldHealth);
}
void URPGAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, Health, OldMaxHealth);
}
void URPGAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldMana) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, Mana, OldMana);
}
void URPGAttributeSet::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, MaxMana, OldMaxMana);
}
OverlayWidgetController
.h文件
// Copyright KimiLiu
#pragma once
#include "CoreMinimal.h"
#include "AbilitySytstem/RPGAttributeSet.h"
#include "UI/WidgetController/RPGWidgetController.h"
#include "OverlayWidgetController.generated.h"
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 URPGUserWidget> MessageData;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
UTexture2D* Image = nullptr;
};
class URPGUserWidget;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAttributeChangeSignature, float, NewValue);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMessageWidgetRowSignature, FUIWidgetRow, Row);
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class AURA_API UOverlayWidgetController : public URPGWidgetController
{
GENERATED_BODY()
public:
virtual void BroadcastInitialValues() override;
virtual void BindCallbacksToDependences() override;
UPROPERTY(BlueprintAssignable, Category="GAS|Attribute")
FOnAttributeChangeSignature OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attribute")
FOnAttributeChangeSignature OnMaxHealthChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attribute")
FOnAttributeChangeSignature OnManaChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Attribute")
FOnAttributeChangeSignature OnMaxManaChanged;
UPROPERTY(BlueprintAssignable, Category="GAS|Messages")
FMessageWidgetRowSignature MessageWidgetRowDelegate;
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Widget Data")
TObjectPtr<UDataTable> MessageWidgetDataTable;
// ~Start Delegate Function
// ~End Delegate Function
// 自定义带<>的模板函数,以后可以常用,指定父类可以获得多个子类的相关函数
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;
}
.cpp文件
.AddLambda可以把函数直接写在()内,不需要多写一个函数,方便了很多,可以后期一次性看到我们绑定的函数是啥,以及这个函数干嘛用的
// Copyright KimiLiu
#include "UI/WidgetController/OverlayWidgetController.h"
#include "AbilitySytstem/RPGAbilitySystemComponent.h"
#include "AbilitySytstem/RPGAttributeSet.h"
//初始化多播执行事件
void UOverlayWidgetController::BroadcastInitialValues()
{
//父类的AttributeSet类型是UAttributeSet不是RPGAttributeSet需要自行Cast
const URPGAttributeSet* RPGAttributeSet = CastChecked<URPGAttributeSet>(AttributeSet);
//执行初始化多播通知
OnHealthChanged.Broadcast(RPGAttributeSet->GetHealth());
OnMaxHealthChanged.Broadcast(RPGAttributeSet->GetMaxHealth());
OnManaChanged.Broadcast(RPGAttributeSet->GetMana());
OnMaxManaChanged.Broadcast(RPGAttributeSet->GetMaxMana());
}
// 绑定(注册)Attribute改变事件,当Attribute的基础值改变的时候,会执行相应Attribute的对应的函数
void UOverlayWidgetController::BindCallbacksToDependences()
{
const URPGAttributeSet* RPGAttributeSet = CastChecked<URPGAttributeSet>(AttributeSet);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
RPGAttributeSet->GetHealthAttribute()).AddLambda(
[this](const FOnAttributeChangeData& Data)
{
OnHealthChanged.Broadcast(Data.NewValue);
}
);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
RPGAttributeSet->GetMaxHealthAttribute()).AddLambda(
[this](const FOnAttributeChangeData& Data)
{
OnMaxHealthChanged.Broadcast(Data.NewValue);
}
);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
RPGAttributeSet->GetManaAttribute()).AddLambda(
[this](const FOnAttributeChangeData& Data)
{
OnManaChanged.Broadcast(Data.NewValue);
}
);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
RPGAttributeSet->GetMaxManaAttribute()).AddLambda(
[this](const FOnAttributeChangeData& Data)
{
OnMaxManaChanged.Broadcast(Data.NewValue);
}
);
Cast<URPGAbilitySystemComponent>(AbilitySystemComponent)->EffectAssetTags.AddLambda(
[this](const FGameplayTagContainer& AssetTags)
{
for(FGameplayTag Tag : AssetTags)
{
// "A.1".MatchesTag("A") will return True, "A".MatchesTag("A.1") will return False
FGameplayTag MessageTag = FGameplayTag::RequestGameplayTag(FName("Message"));
if (Tag.MatchesTag(MessageTag))
{
FUIWidgetRow* Row = GetDataTableRowByTag<FUIWidgetRow>(MessageWidgetDataTable, Tag);
MessageWidgetRowDelegate.Broadcast(*Row);
}
}
}
);
}
// ~Delegate Function Start
// ~Delegate Function End