UE5多人MOBA+GAS 5、使用GAS技能系统

文章目录

  • 使用GAS
    • 添加技能组件和属性类
    • 基类角色中创建技能组件和属性类
  • 属性集 Attribute Set的设置
    • BaseValue 和 CurrentValue
    • Attribute Set
  • 调试方法
    • 1、命令行
    • 2、比较好看的界面


关于GAS的详细介绍点击此处跳转

使用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的一个恒值, 而当前值 CurrentValueBaseValue 加上GameplayEffects的临时修改值。 例如,你的角色有个移动速度movespeed的属性AttributeBaseValue为600 单位/秒。由于没有任何GameplayEffects修改movespeed,所以其CurrentValue也是600单位/秒。如果角色获取了一个50单位/秒的速度加成(BUFF),BaseValue仍然保持在600单位/秒,而CurrentValue 将等于650单位/秒=600 + 50。当移动速度加成BUFF过期后,CurrentValue 将恢复成BaseValue 600单位/秒。

通常刚接触GAS的新手会将BaseValue理解为或当作是一个属性的最大值。这是不正确的, 能够被技能或者UI使用的Attribute的最大值应该是另一个单独的Attribute。 对于硬编码的最大值和最小值,可以通过FAttributeMetaDataDataTable定义,其可以设置最大值和最小值,但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就可能看见属性(我个人比较喜欢这个)
在这里插入图片描述
关于没有小数字按键的问题,这个应该没改按键
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值