文章目录
- 使用GAS
- 添加技能组件和属性类
- 基类角色中创建技能组件和属性类
- 属性集 Attribute Set的设置
- BaseValue 和 CurrentValue
- Attribute Set
- 调试方法
- 1、命令行
- 2、比较好看的界面
使用GAS
build.cs文件中添加三个模块
"GameplayAbilities", "GameplayTags", "GameplayTasks"
去插件中打开Gameplay Abilities

添加技能组件和属性类
继承AbilitySystemComponent类,添加一个CAbilitySystemComponent类


再继承AttributeSet这个属性类,创建CAttributeSet类


基类角色中创建技能组件和属性类
在CCharacter文件中添加GAS相关
// 幻雨喜欢小猫咪
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h" // IAbilitySystemInterface接口的头文件
#include "GAS/CAbilitySystemComponent.h"
#include "GAS/CAttributeSet.h"
#include "CCharacter.generated.h"
UCLASS()
class ACCharacter : public ACharacter, public IAbilitySystemInterface
{
#pragma region GAS组件相关
public:
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
private:
UPROPERTY(VisibleDefaultsOnly, Category = "Gameplay Ability")
TObjectPtr<UCAbilitySystemComponent> CAbilitySystemComponent;
UPROPERTY()
TObjectPtr<UCAttributeSet> CAttributeSet;
#pragma endregion
};
// 幻雨喜欢小猫咪
#include "Crunch/Private/Character/CCharacter.h"
#include "Components/SkeletalMeshComponent.h"
ACCharacter::ACCharacter()
{
PrimaryActorTick.bCanEverTick = true;
// 禁用网格的碰撞功能
GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
CAbilitySystemComponent = CreateDefaultSubobject<UCAbilitySystemComponent>(TEXT("CAbilitySystemComponent"));
CAttributeSet = CreateDefaultSubobject<UCAttributeSet>(TEXT("CAttributeSet"));
//CAbilitySystemComponent->SetIsReplicated(true); //设置组件用于在网络上复制,经过我的测试本来就是true
}
UAbilitySystemComponent* ACCharacter::GetAbilitySystemComponent() const
{
return CAbilitySystemComponent;
}
属性集 Attribute Set的设置
BaseValue 和 CurrentValue
取自于此的4.3.2
一个Attribute由两个值构成 - 一个基值 BaseValue 和一个当前值CurrentValue. 基值BaseValue是属性 Attribute的一个恒值, 而当前值 CurrentValue 是 BaseValue 加上GameplayEffects的临时修改值。 例如,你的角色有个移动速度movespeed的属性Attribute其BaseValue为600 单位/秒。由于没有任何GameplayEffects修改movespeed,所以其CurrentValue也是600单位/秒。如果角色获取了一个50单位/秒的速度加成(BUFF),BaseValue仍然保持在600单位/秒,而CurrentValue 将等于650单位/秒=600 + 50。当移动速度加成BUFF过期后,CurrentValue 将恢复成BaseValue 600单位/秒。
通常刚接触GAS的新手会将BaseValue理解为或当作是一个属性的最大值。这是不正确的, 能够被技能或者UI使用的Attribute的最大值应该是另一个单独的Attribute。 对于硬编码的最大值和最小值,可以通过FAttributeMetaData的DataTable定义,其可以设置最大值和最小值,但Epic注意这个结构体"work in progress"。详见AttributeSet.h。 为了清除困惑,强烈建议用于技能或者UI上的最大值Attribute是一个单独的Attribute,并且FAttributeMetaData中的最大值和最小值仅用于属性值的限定(Clamping)。CurrentValue的属性值限定将会在PreAttributeChange()谈论,BaseValue的属性值限定将会在PostGameplayEffectExecute()讨论,其执行由GameplayEffects触发。
立即(Instant) GameplayEffects将永久改变BaseValue,而持续(Duration) 和永恒(Infinite) GameplayEffects 将改变CurrentValue。周期性(Periodic )GameplayEffects像立即(Instant) GameplayEffects一样将改变BaseValue
Attribute Set
.h
// 幻雨喜欢小猫咪
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "AttributeSet.h"
#include "CAttributeSet.generated.h"
//宏的设置,编译时会默认给变量生成相应的Getter以及Setter函数,当前设置会生成四个函数,获取属性,获取值,设置值,以及初始化值。
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
/**
*
*/
UCLASS()
class UCAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
// 用于声明哪些属性需要在网络中进行复制
virtual void GetLifetimeReplicatedProps( TArray< class FLifetimeProperty > & OutLifetimeProps ) const override;
// 当Attribute的CurrentValue被改变之前调用
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
// 仅在instant Gameplay Effect使Attribute的BaseValue改变时触发
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData & Data) override;
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health)
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS(UCAttributeSet, Health)
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth)
FGameplayAttributeData MaxHealth;
ATTRIBUTE_ACCESSORS(UCAttributeSet, MaxHealth)
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Mana)
FGameplayAttributeData Mana;
ATTRIBUTE_ACCESSORS(UCAttributeSet, Mana)
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxMana)
FGameplayAttributeData MaxMana;
ATTRIBUTE_ACCESSORS(UCAttributeSet, MaxMana)
UFUNCTION()
void OnRep_Health(const FGameplayAttributeData& OldHealth);
UFUNCTION()
void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
UFUNCTION()
void OnRep_Mana(const FGameplayAttributeData& OldMana);
UFUNCTION()
void OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana);
};
cpp
// 幻雨喜欢小猫咪
#include "GAS/CAttributeSet.h"
#include "Net/UnrealNetwork.h"
#include "GameplayEffectExtension.h"
void UCAttributeSet::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, Health, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, Mana, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, MaxMana, COND_None, REPNOTIFY_Always);
}
void UCAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
if (Attribute == GetHealthAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
}
if (Attribute == GetManaAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMana());
}
}
void UCAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
SetHealth(FMath::Clamp(GetHealth(), 0, GetMaxHealth()));
}
if (Data.EvaluatedData.Attribute == GetManaAttribute())
{
SetMana(FMath::Clamp(GetMana(), 0, GetMaxMana()));
}
}
void UCAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, Health, OldHealth);
}
void UCAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, MaxHealth, OldMaxHealth);
}
void UCAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldMana)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, Mana, OldMana);
}
void UCAttributeSet::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, MaxMana, OldMaxMana);
}
宏的设置,编译时会默认给变量生成相应的Getter以及Setter函数,当前设置会生成四个函数,获取属性,获取值,设置值,以及初始化值。
宏
ATTRIBUTE_ACCESSORS(ClassName, PropertyName)
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName)
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName)
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName)
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
扩展预览
static FGameplayAttribute GetMaxManaAttribute()
{
static FProperty* Prop = FindFieldChecked<FProperty>(UCAttributeSet::StaticClass(),
((void)sizeof(UEAsserts_Private::GetMemberNameCheckedJunk(
((UCAttributeSet*)0)->MaxMana)), FName(L"MaxMana")));
return Prop;
}
__forceinline float GetMaxMana() const { return MaxMana.GetCurrentValue(); }
__forceinline void SetMaxMana(float NewVal)
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if (((!!(!!(AbilityComp))) || (::UE::Assert::Private::ExecCheckImplInternal(
[]() __declspec(code_seg(".uedbg")) -> std::atomic<bool>& {
static std::atomic<bool> bExecuted = false;
return bExecuted;
}(), false, "CAttributeSet. h", 36, "AbilityComp") && []()
{
(__nop(), __debugbreak());
return false;
}()))) { AbilityComp->SetNumericAttributeBase(GetMaxManaAttribute(), NewVal); };
}
__forceinline void InitMaxMana(float NewVal)
{
MaxMana.SetBaseValue(NewVal);
MaxMana.SetCurrentValue(NewVal);
}
变量的OnRep 函数调用GAMEPLAYATTRIBUTE_REPNOTIFY 宏才能使用预测系统。
宏
GAMEPLAYATTRIBUTE_REPNOTIFY(ClassName, PropertyName, OldValue)
{
static FProperty* ThisProperty = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName));
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(ThisProperty), PropertyName, OldValue);
}
扩展预览
{
static FProperty* ThisProperty = FindFieldChecked<FProperty>(UCAttributeSet::StaticClass(),
((void)sizeof(
UEAsserts_Private::GetMemberNameCheckedJunk(
((UCAttributeSet*)0)->Health)), FName(
L"Health")));
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(
FGameplayAttribute(ThisProperty), Health, OldHealth);
}
This is a helper macro that can be used in RepNotify functions to handle attributes that will be predictively modified by clients.
void UMyHealthSet::OnRep_Health(const FGameplayAttributeData& OldValue) { GAMEPLAYATTRIBUTE_REPNOTIFY(UMyHealthSet, Health, OldValue); }
Attribute 需要被添加到GetLifetimeReplicatedProps()中,COND_None 为触发没有条件限制,REPTNOTIFY_Always 告诉 OnRep方法在本地值和服务器下发的值即使已经相同也会触发(为了预测),默认情况下OnRep不会触发
宏
DOREPLIFETIME_CONDITION_NOTIFY(c, v, cond, rncond)
{
static_assert(cond != COND_NetGroup, "COND_NetGroup cannot be used on replicated properties. Only when registering subobjects");
FDoRepLifetimeParams LocalDoRepParams;
LocalDoRepParams. Condition = cond;
LocalDoRepParams. RepNotifyCondition = rncond;
DOREPLIFETIME_WITH_PARAMS(c,v,LocalDoRepParams);
}
扩展预览
{
static_assert(COND_None != COND_NetGroup,
"COND_NetGroup cannot be used on replicated properties. Only when registering subobjects");
FDoRepLifetimeParams LocalDoRepParams;
LocalDoRepParams.Condition = COND_None;
LocalDoRepParams.RepNotifyCondition = REPNOTIFY_Always;
{
static_assert(ValidateReplicatedClassInheritance<UCAttributeSet, ThisClass>(),
"UCAttributeSet" "." "Health" " is not accessible from this class.");
FProperty* ReplicatedProperty = GetReplicatedProperty(StaticClass(), UCAttributeSet::StaticClass(),
((void)sizeof(UEAsserts_Private::GetMemberNameCheckedJunk(
((UCAttributeSet*)0)->Health)), FName(L"Health")));
__pragma (warning(push))
__pragma (warning(disable: 4995))
__pragma (warning(disable: 4996))
RegisterReplicatedLifetimeProperty(ReplicatedProperty, OutLifetimeProps,
FixupParams<decltype(UCAttributeSet::Health)>(LocalDoRepParams));
__pragma (warning(pop))
};
}
调试方法
1、命令行
按下~键,输入AbilitySystem.DebugAttribute然后空格输入你要看的属性(我不知道怎么弄走这个东西呢QAQ)

回车后就会出现对应的值

2、比较好看的界面
点击Enter左边的按键可以唤出此面板按下小数字键的3就可能看见属性(我个人比较喜欢这个)

关于没有小数字按键的问题,这个应该没改按键

2772

被折叠的 条评论
为什么被折叠?



