UE GAS学习

【Unreal】虚幻GAS系统快速入门-CSDN博客

GameplayTags

FGameplayTags是一种层级标签,如Parent.Child.GrandChild。
通过GameplayTagManager进行注册。替代了原来的Bool,或Enum的结构,可以在玩法设计中更高效地标记对象的行为或状态。
GameplayTags是一个内置的插件,不属于GAS。

但是GAS会大量使用Tag

Gameplay Ability System

GAS主要包含以下内容:

  • ASC(Ability System Component)主要组件,由C++编写,代码里有很多方法是蓝图未实现的。
  • GA(Gameplay Abilities)角色的技能,包括攻击、疾跑、施法、翻滚、使用道具等,但不包括基础移动和UI。
  • AS(Attribute Set)角色身上可以用float表示的属性,如生命值、体力值、魔力值等。
  • GE(Gameplay Effects)用于修改属性,如增加50移动速度10s;还能配合GA实现更多玩法。
  • GC(Gameplay Cues)播放特效、音效等。

Ability System Component 

Ability System Component(ASC)是整个GAS的基础组件。


ASC本质上是一个UActorComponent,用于处理整个框架下的交互逻辑,包括使用技能 (GameplayAbility)、包含属性(AttributeSet)、处理各种效果(GameplayEffect)。


所有需要应用GAS的对象(Actor),都必须拥有GAS组件。


拥有ASC的Actor被称为ASC的OwnerActor,ASC实际作用的Actor叫做AvatarActor。ASC可以被赋予某个角色ASC,也可以被赋予PlayerState(可以保存死亡角色的一些数据)

简单来说,ASC是一种角色组件,负责和GA、GE、AS打交道。

如果Character需要销毁再重新生成,如MOBA游戏角色死亡后泉水复活,那么ASC可以放在PlayerState上避免随着角色一同销毁。此时的OwnerActor是PlayerState,AvatarActor则是Character。

注意事项

项目Build.cs文件的PrivateDependencyModuleNames里加上“GameplayAbilities”,“GameplayTags”,“GameplayTasks”三个模块。 

 组件声明

#include "AbilitySystemComponent.h"public:UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities")class UAbilitySystemComponent* AbilitySystemComponent;

  组件实例化

//实例化ASC
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystem"));

通过 IAbilitySystemInterface接口,并实现GetASC函数

#include "AbilitySystemInterface.h"class ARPG_UNREAL_API ACharacterBase : public ACharacter, public IAbilitySystemInterfacepublic:UAbilitySystemComponent* GetAbilitySystemComponent()const override;
UAbilitySystemComponent* ACharacterBase::GetAbilitySystemComponent()const{return AbilitySystemComponent;}

Gameplay Ability

Gameplay Ability(GA)标识了游戏中一个对象(Actor)可以做的行为或技能。能力(Ability)可以是普通攻击或者吟唱技能,可以是角色被击飞倒地,还可以是使用某种道具,交互某个物件,甚至跳跃、飞行等角色行为也可以是Ability。


Ability可以被赋予对象或从对象的ASC中移除,对象同时可以激活多个GameplayAbility。*基本的移动输入、UI交互行为则不能或不建议通过GA来实现

注意事项 

        角色需要拥有GA后,才能使用GA。

GA的使用分为实例化释放两个过程,前者主要是生成一个FGameplayAbilitySpec对象,并为一部分非公有(非静态)属性赋值,如当前GA的等级。后者操作的实际对象则为Spec。

可以把Spec理解为GA的实例,GE等其他类也有相似的概念。

通常来说,使用GA时不用去考虑两个过程的区别,除非你需要在实例化Spec后,手动修改一些在GA类上定义好的属性再去手动释放。在GE篇会详细介绍,用于实现技能的冷却、消耗

添加GA 

1.在角色类中创建一个数组,游戏启动时自动添加数组里的GA
(注意:使用这一种方法不易控制每个GA的初始等级。)

1、在角色头文件声明数组:

public:// 将在游戏启动时被赋予角色的Abilities数组UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Abilities")TArray<TSubclassOf<class UGameplayAbility>> PreloadedAbilities;

2.在角色的BeginPlay()里遍历数组,使用AbilitySystemComponent->GiveAbility()添加Ability:

Super::BeginPlay();
if (AbilitySystemComponent != nullptr)
{
//初始化技能
if (PreloadedAbilities.Num() > 0){for (auto i = 0; i < PreloadedAbilities.Num(); i++){if (PreloadedAbilities[i] != nullptr){// FGameplayAbilitySpec是GA的实例,其构造函数的第二个参数代表GA的等级,这里暂令其全部为1AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(PreloadedAbilities[i].GetDefaultObject(), 1));}}}//初始化ASC
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}

3.在角色蓝图的Details面板找到数组,填好GA。

注意事项: 

        AbilitySystemComponent->GiveAbility()方法在蓝图中无法使用

蓝图中动态添加Ability,我们需要在蓝图中实现自己的GiveAbility()

.h

public://添加Ability
UFUNCTION(BlueprintCallable, Category = "Ability System")
void GiveAbility(TSubclassOf<UGameplayAbility> Ability, int32 Level = 1);

.cpp 

void ACharacterBase::GiveAbility(TSubclassOf<UGameplayAbility> Ability, int32 Level)
{if (AbilitySystemComponent){if (HasAuthority() && Ability){AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(Ability, Level));}
AbilitySystemComponent->InitAbilityActorInfo(this, this);}
}

使用GE添加GA
        新建一个GE,在Granted Abilities条目里添加的GA都会在GE被Apply到角色身上时赋予(Grant)。

        然后在蓝图里调用Apply GameplayEffect to Self节点即可(这里的Level是GE的等级,不是GA)。

制作GA

        在内容浏览器右键,创建Gameplay→Gameplay技能蓝图:

继承自GameplayAbility:

最下面两个是内置的GA,上面的是自制的GA

 

一般GA要做的事有:

  • 设置GA的Tag、CD、Cost等属性。
  • 获取必要信息,主要通过Get Actor Info。如果是通过Event调用的GA(使用Activate Ability From Event节点作为输入),还可以通过Gameplay Event Data获取。
  • 编写逻辑,如播放动画、应用GE、应用冲量等。
  • 一定不要忘了EndAbility。

调用GA

主动调用

在蓝图中主要有by Class和by Tag两种调用方法。

byClass一次只能Activate一个GA,byTag可以Activate任意多个GA,配合Tag容器使用。

如果使用EnhancedInputAction插件来管理输入,要注意在某些设置下Trigger会每帧都进行输出

只要能获取ASC,就可以在任何地方调用GA,比如行为树Task蓝图,甚至在GA蓝图中调用其他GA。

被动调用

Trigger可以理解为一个Tag,当ASC组件收到一个Trigger时,就会自动调用所有拥有该Trigger的GA。

Trigger的Tag在GA的Details面板中设置。

Trigger的触发方式有三种,分别是:

  • Gameplay Event:当Owner收到一个带有Tag的Gameplay Event(不是Gameplay Effect的GE!)时调用一次GA,此时Owner不会拥有对应的Tag
  • Owner Tag Added:当Owner获取对应Tag的时候调用一次GA。
  • Owner Tag Present:当Owner拥有Tag时调用GA,失去Tag时移除。

一般使用第一种方法,并配合SendGameplayEventToActor节点使用,如下图所示。(这张图是很久以前截的,Tag建议以Event开头。)

受击效果的例子,发送一个Tag为Hit的Event给碰撞检测到的Actor

使用Gameplay Event调用的好处是,可以传入数据(Payload),是除了Get Actor Info外的另一种信息传递方法。

此时应该删除ActiveAbility节点,转而使用ActivateAbilityFromEvent事件。(不要通过在左上角重载函数的方式,右键空白处搜索才是对的。

设置GA触发条件

GA的标签

可以限制各种技能的相互关系,比如受击时候不能翻滚。

这时候Tag的父子层级关系设计就尤为重要,可以把受击时不能释放的技能都放在同一个父层级下。

Tag建议以Ability开头。

Ability Tags:该GA的标签。

Cancel Abilities with Tag:激活该GA时,打断其他拥有所选标签的GA。

Block Abilities with Tag:激活该GA时,阻止激活拥有所选标签的GA(已经激活的不会被中断)。

Activation Owned Tags:激活该GA时,赋予ASC所选GA。

Activation Required Tags:激活GA时,ASC需要的标签。

Activation Blocked Tags:激活GA时,ASC不能有的标签。

Source Required Tags:激活GA时,Source需要的标签。

Source Blocked Tags:激活GA时,Source不能有的标签。

Target Required Tags:激活GA时,Target需要的标签。

Target Blocked Tags:激活GA时,Target不能有的标签。

冷却与消耗

在GA的Details面板的Cost和Cooldown条目中选择对应的GE即可。

一个Cooldown GE仅需满足以下要求:

  • 为Has Duration类型,Duration Magnitude计算方式为Set By Caller或Custom Calculation Class。
  • Granted Tags为技能的冷却Tag,如Cooldown.skill1。

在Cooldown GE持续期间,玩家的ASC组件就会携带对应技能的Cooldown Tag,本质是通过Tag来限制的。

*冷却Tag建议以Cooldown开头统一管理。

一个Cost GE仅需满足以下要求:

  • 为Instant类型。
  • 有一个或多个Modifier去修改对应的属性,计算方式为Custom Calculation Class。

每个GA都要写一遍Cost和CD的GE,非常麻烦

优化 

在实例化生成GE Spec时,修改其Cost和Cooldown属性后再将其应用。

首先创建一个GA基类,添加CD时长、Cost数值(包括生命值和法力值两种类型的Cost)、以及Cooldown Tag等属性,并重载GetCooldownTags、ApplyCooldown、GetCostGameplayEffect三个方法。

GameplayAbilityBase.h

#pragma once#include "CoreMinimal.h"
#include "Abilities/GameplayAbility.h"
#include "GameplayAbilityBase.generated.h"/****/
UCLASS()
class ARPG_UNREAL_API UGameplayAbilityBase : public UGameplayAbility
{GENERATED_BODY()
public:UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Cooldowns")FScalableFloat CooldownDuration;UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Cooldowns")FGameplayTagContainer CooldownTags;UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Costs")FScalableFloat HealthCost;// 根据需要可以设置多种类型CostUPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Costs")FScalableFloat ManaCost;UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Costs")FScalableFloat StaminaCost;// Temp container that we will return the pointer to in GetCooldownTags().// This will be a union of our CooldownTags and the Cooldown GE's cooldown tags.
UPROPERTY(Transient)FGameplayTagContainer TempCooldownTags;// Return the union of our Cooldown Tags and any existing Cooldown GE's tags.
virtual const FGameplayTagContainer* GetCooldownTags() const override;// Inject our Cooldown Tags and to add the SetByCaller to the cooldown GameplayEffectSpec.
virtual void ApplyCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const override;virtual UGameplayEffect* GetCostGameplayEffect() const override;};

GameplayAbilityBase.cpp

#include "ARPG_Unreal/Public/GameplayAbilitySystem/GameplayAbilityBase.h"const FGameplayTagContainer* UGameplayAbilityBase::GetCooldownTags() const
{FGameplayTagContainer* MutableTags = const_cast<FGameplayTagContainer*>(&TempCooldownTags);MutableTags->Reset();
// MutableTags writes to the TempCooldownTags on the CDO so clear it in case the ability cooldown tags change (moved to a different slot)
const FGameplayTagContainer* ParentTags = Super::GetCooldownTags();if (ParentTags){MutableTags->AppendTags(*ParentTags);}MutableTags->AppendTags(CooldownTags);return MutableTags;
}void UGameplayAbilityBase::ApplyCooldown(const FGameplayAbilitySpecHandle Handle,const FGameplayAbilityActorInfo* ActorInfo,const FGameplayAbilityActivationInfo ActivationInfo) const
{UGameplayEffect* CooldownGE = GetCooldownGameplayEffect();if (CooldownGE){FGameplayEffectSpecHandle SpecHandle =MakeOutgoingGameplayEffectSpec(CooldownGE->GetClass(), GetAbilityLevel());SpecHandle.Data.Get()->DynamicGrantedTags.AppendTags(CooldownTags);SpecHandle.Data.Get()->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Cooldown")),CooldownDuration.GetValueAtLevel(GetAbilityLevel()));ApplyGameplayEffectSpecToOwner(Handle, ActorInfo, ActivationInfo, SpecHandle);}
}UGameplayEffect* UGameplayAbilityBase::GetCostGameplayEffect() const
{return Super::GetCostGameplayEffect();
}

然后创建继承UGameplayModMagnitudeCalculation,创建对应属性的Cost MMC,这里仅展示法力值消耗,Stamina消耗同理。

ManaMMC.h

#pragma once#include "CoreMinimal.h"
#include "GameplayModMagnitudeCalculation.h"
#include "ManaMMC.generated.h"/****/
UCLASS()
class ARPG_UNREAL_API UManaMMC : public UGameplayModMagnitudeCalculation
{GENERATED_BODY()virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
};

ManaMMC.cpp

#include "GameplayAbilitySystem/ManaMMC.h"#include "GameplayAbilitySystem/GameplayAbilityBase.h"float UManaMMC::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{const UGameplayAbilityBase* Ability = Cast<UGameplayAbilityBase>(Spec.GetContext().GetAbilityInstance_NotReplicated());if (!Ability){return 0.0f;}return Ability->ManaCost.GetValueAtLevel(Ability->GetAbilityLevel());
}

最后写一个通用的Cost和CD GE,所有的GA都使用这两个GE创建Spec。

                                                Cost GE

                                             Cooldown GE

注意上面的Data Tag并不等同于Cooldown Tag,只是用于告诉GE的修改器(Modifier)需要修改(Modify)的Data是什么。Cooldown Tag才是CD期间拥有的Tag,以Cooldown开头。

之后创建一个GA蓝图基类,之后所有的GA都继承自这个基类,配置好CD、Tag和Cost,然后调用Commit Ability节点就好了。如果不需要Cost或CD,最好取消选择Cooldown GE Class和Cost GE Class,以避免当魔力值归零时无法释放0消耗技能的问题。

                                                                       基类设置

                                                                  具体GA配置

Ability Task

GA是在一帧内完成的,如果想要实现类似Wait的异步逻辑需要使用Task。

图中所示就是Ability Task,是基于原生的Gameplay Task实现的。

可以看见,GAS内置了许多Task,图中用的是一个播放蒙太奇的Task(注意与UE原生的播放蒙太奇节点不同,在GAS系统中最好使用PlayMontageAndWait)。

AttributeSet

AttributeSet负责定义和持有属性,并且管理属性的变化,包括网络同步。需要在Actor中被添加为成员变量,并注册到ASC(C++)。


一个ASC可以拥有一个或多个(不同的)AttributeSet,因此可以角色共享一个很大的 AttributeSet,也可以每个角色按需添加AttributeSet。


可以在属性变化前(PreAttributeChange)后(PostGameplayEffectExecute)处理相关逻辑,可以通过委托的方式绑定属性变化。

正如字面意思,AS是Attribute的集合。

Attribute就是HP、MP、Speed、ATK等可以用float表示的属性

因为Attribute是包含了两个float变量的结构体,分别是Base Value和Current Value。

Base Value表示基础值,Current Value表示临时值。

如临时增加100生命值10s,改变的就是Current Value,10s后自动变回Base Value。

注意事项

        AS只能使用C++创建。

 AS添加

创建AttributeSetBase类,这里需要使用AbilitySystemComponent.h的宏ATTRIBUTE_ACCESSORS()。

对每一个FGameplayAttributeData都应用一遍宏。

这里创建Health和MaxHealth作为示范。

AttributeSetBase.h

#pragma once#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "AttributeSetBase.generated.h"// Uses macros from AttributeSet.h#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)/****/
UCLASS()
class ARPG_UNREAL_API UAttributeSetBase : public UAttributeSet
{GENERATED_BODY()public:// AttributesUPROPERTY(VisibleAnywhere, BlueprintReadWrite);FGameplayAttributeData Health;ATTRIBUTE_ACCESSORS(UAttributeSetBase, Health);UPROPERTY(VisibleAnywhere, BlueprintReadWrite);FGameplayAttributeData MaxHealth;ATTRIBUTE_ACCESSORS(UAttributeSetBase, MaxHealth);};

 

最后在ASC组件里指定好就可以使用了。

                          Default Starting Table是用于属性初始化用的

AS初始化

AS可以通过GE和DataTable两种不同方式初始化,Epic推荐使用GE。

通过GE初始化

创建一个GE,命名为GE_InitAttributes,找到Gameplay Effect条目。

一个Modifiers对应一个Attributes。

添加新的Modifier,选择要修改的属性,Modifier Op(修改方式)选择Override。

Modifier Magnitude(修改值)选择Scalable Float,填入想设置的默认值。

然后在蓝图中Apply该GE即可。

通过DataTable初始化 

创建一个DataTable,行结构选择AttributeMetaData,其格式如下。

可以使用Excel做好后保存为csv文件再快速导入。

注意这里的最大最小值没有任何作用,为未完成功能,实现方法见4.5。

因此最好单独创建一个MaxHealth属性

属性的名称需要带上完整类名

然后找到Character的ASC组件,在Attribute Test条目填上表格即可。

AS获取 

搜索Get Attribute即可,Current Value和Base Value都可以获得。

可以通过ASC组件调用,也可以使用GAS的蓝图函数库里的函数。

监听Attribute修改事件 

PreAttributesChange和PostGameplayEffectExecute

AttributeSet提供了两个方法用于监听Value的改变:

  • PreAttributeChange:用于Attribute的Current Value被改变前调用,对应Infinite和Has Duration的GE。
  • PostGameplayEffectExecute:用于Base Value改变后调用,对应InstantGE。
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;

这两个事件适用于Clamp属性,确保其不超出临界值。

void UAttributeSetBase::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);if (Attribute == GetHealthAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth());
}
}// 这个方法也行,但是需要"GameplayEffectExtension.h"
void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{Super::PostGameplayEffectExecute(Data);if(Data.EvaluatedData.Attribute == GetHealthAttribute()){SetHealth(FMath::Clamp(GetHealth(), 0.0f, GetMaxHealth()));}
}

 GetGameplayAttributeValueChangeDelegate

如果想要监听Attribute的变化以更新UI,则不适合用上面的方法,应该在角色类中创建一个回调,以及蓝图事件:

CharacterBase.h

// Attribute Change Callbacks
void OnHealthChanged(const FOnAttributeChangeData& Data);// Attribute Change Event in Blueprint
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangeEvent, float, NewHealth);
UPROPERTY(BlueprintAssignable, Category="Ability")
FOnHealthChangeEvent HealthChangeEvent;

CharacterBase.cpp

void ACharacterBase::OnHealthChanged(const FOnAttributeChangeData& Data)
{HealthChangeEvent.Broadcast(Data.NewValue);
}

然后在BeginPlay()里将其注册到ASC:

void ACharacterBase::BeginPlay()
{Super::BeginPlay();if (AbilitySystemComponent != nullptr){//初始化技能...//初始化ASC...//注册Attribute变化事件AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UAttributeSetBase::GetHealthAttribute()).AddUObject(this, &ACharacterBase::OnHealthChanged);}
}

之后就可以从蓝图调用生命值变化事件了。

Gameplay Effect 

Gameplay Effect(GE)是Ability对自己或他人产生影响的途径。GE通常可以被理解为我们游戏中的Buff。比如增益/减益效果(修改属性)。


但是GAS中的GE也更加广义,释放技能时候的伤害结算,施加特殊效果的控制、霸体效果 (修改GameplayTag)都是通过GE来实现的。


GE相当于一个可配置的数据表,不可以添加逻辑。开发者创建一个UGameplayEffect的派 生蓝图,就可以根据需求制作想要的效果。

GE就是一张数据表,不负责逻辑处理,定义Attribute修改的值。

GE是修改Attribute的唯一渠道!

其工作流程可以简单分为以下几步:

  1. 创建一个实例Spec。
  2. (可选)修改Spec的一些值。
  3. 如果允许,应用(Apply)GE,但是Attribute仍未被修改。
  4. 如果允许,使Modifier生效,修改Attribute。
  5. 满足条件后,移除该Spec。

其功能非常多且强大,提供了非常多的可配置项。来一张图感受一下:

GE的配置项可以满足绝大多数游戏的需求,尤其是MOBA游戏和RPG游戏。

比较重要的功能有嵌套调用GE赋予GA调用GC

此外也能根据等级计算数据、实现多层GE叠加设置GE应用的条件几率等。

GE配置项讲解

Gameplay Effect

最核心的配置项。

Duration Policy:GE的持续类型,有三种。

  • Instand:立即改变Base Value(扣血)。
  • Infinite:永久改变Current Value(按下疾跑修改速度)只能通过GA或ASC取消。
  • Has Duration:临时修改Current Value(临时Buff)。

Modifiers:选择你要修改的Attribute,支持数值等级曲线和Tag,会单独讲。

Executions:同样也是修改属性,支持更复杂的运算。

Conditional Gameplay Effects:当GE成功应用时,可以应用其他GE,嵌套调用GE的方法之一

如果是Has Duration的GE,那么我们需要设定Duration的时长,称为Duration Magnitude。

Period

设置GE的触发周期,仅有Infinite和Has Duration的GE才显示前两项设置

Period:
如果是Infinite模式,加上Period后等价于周期执行的Instant
如果是Has Duration模式,就是普通的周期重复。(也有说法是周期执行的Instance,待证实。)

Execute Periodic Effect on Application:t=0的时候是否触发。(如LOL中点燃技能就是使用后立即造成伤害,之后每秒应用一次)。

Periodic Inhibition Policy:GE中断并恢复后的处理方式。

  • Never Reset:从被打断时的位置开始计算周期,相当于暂停再播放。
  • Reset Period:从0开始计算周期。
  • Execute and Reset Period:打断时立即执行一次,下次从0开始计算周期。
 Application 

设置GE的应用概率和条件。

概率支持曲线图表。

条件可以简单地用Tag去限制,也可以用Application Requirement进行更复杂的逻辑判断。

需要添加一个自定义的Custom Application Requirement(CAR)蓝图类,重载里面的唯一方法,如下图所示。

官方文档推荐在以下情况使用CAR蓝图类:

  • Target需要有一定数量的属性时;
  • Target需要GE堆叠到一定数量时;
  • 除此之外CARs还能够做更多事情,比如检查Target是否应用了一个GameplayEffect 的实例,在应用一个新实例时如果同类型的实例已存在则只改变其持续时间(CanApplyGameplayEffect()要返回false)。
Stacking 

用于叠加多个GE的效果,仅能用于Infinite和Has Duration的GE

Stack Limit Count:最大层数。

Stacking Type:叠加栈在目标身上or施法者身上。

举个例子,假设层数为3,如果是by Target模式,那么3个敌人对我释放的Debuff只能叠三层。

如果是by Source模式,那么3个敌人可以对我叠加9层Debuff。

每层Effect如果是Modifiers来计算,则为直接叠加的效果,比如用Modifiers来增加3攻击力,则第一层为增加3攻击力,第二层为增加6攻击力,第三层为增加9攻击力,而如果需要根据层数不同而改变增加的值,则需要使用Executions。

Stack Duration Refresh Policy:Apply新GE时是否刷新持续时间,注意溢出的Apply也会刷新,想关闭可以在下面的Overflow条目关闭。

Stack Period Reset Policy:同上,是否刷新周期。

Stack Expiration Policy:当一层GE的Duration到期后的处理方式。

  • Clear Entire Stack:清空全部层数,如LOL征服者。
  • Remove Single Stack and Refresh Duration:清空一层,如LOL致命节奏。
  • Refresh Duration:不清空,相当于无限长的Duration,但可以通过调用FActiveGameplayEffectsContainer::OnStackCountChange(FActiveGameplayEffect& ActiveEffect, int32 OldStackCount, int32 NewStackCount)方法来自己处理细节,如一次掉两层。
Overflow 

可以设置Stack溢出会Apply的GE。通过GE应用GE的方法之一,需要配合Stacking来使用。

Deny Overflow Application:如果为True,则溢出的Apply不会刷新Duration。

Clear Stack On Overflow:字面意思,需要勾选上一个选项后才能选中。

Expiration

当GE的Duration被打断或结束时的行为。通过GE应用GE的方法之一,仅能用于Has Duration的GE。

Premature Expiration Effect Classes:打断时Apply的GE。

Routine Expiration Effect Classes:正常结束时Apply的GE。

Immunity

Immunity和Tag类似,也可以用来限制GE。

通过Tag匹配来实现,匹配的目标是Target的ASC组件以及拥有的GA。

如果拥有Require Tags的所有Tag,并且没有Ignore Tags的所有Tag,则认为匹配成功,该GE不会被Apply。

和Tags相比,Immunity提供了一个回调UAbilitySystemComponent::OnImmunityBlockGameplayEffectDelegate。

下面的Granted Application Immunity Query是更高级的匹配,但是更消耗性能。

比较特殊的是最后三个选项,分别是根据GE修改的Attribute匹配、根据GE来源匹配以及根据GE的类匹配。

Tags

和GA的Tag条目类似,设置各种限制条件。

GameplayEffectAssetTag:GE的Tag。Combined Tag为计算结果,不可编辑,计算方式是继承的Tag+Added-Removed。

GrantedTags:GE会赋予目标ASC的Tag,仅适用于Infinite和Has Duration的GE。

Application Tag Requirements:GE满足Tag条件才能应用(Apply)。

Ongoing Tag Requirements:GE满足Tag条件才能修改值(Modifier or Execution)。通过这项设置GE可以仅Apply而不修改值,仅适用于Infinite和Has Duration的GE

Removal Tag Requirements:GE满足Tag条件就会被移除。

Remove Gameplay Effects with Tags:Apply后移除指定Tag的GE。

Remove Gameplay Effect Query:上面一条的高级版,可以匹配GE的类(Effect Definition),匹配来源(Effect Source)以及匹配GE修改的属性(Modifying Attribute),移除成功匹配的GE。

Display 

与特效相关的设置,调用Gameplay Cue的方式之一

Granted Abilities

使用GE添加GA的方式。仅支持Infinite和Has Duration的GE。

Level:GA的等级。

Input ID:如果使用旧版输入,每一个操作映射都对应着一个枚举值,输入对应的枚举值就可以将这个新GA绑定到输入上。

Removal Policy:设置当GE被移除时,GA是否要移除。

  • Cancel Ability Immediately:移除,并触发事件EndAbility。
  • Remove Ability on End:移除,但是不触发EndAbility。
  • Do Nothing:GA不会被移除。

Modifier与Execution 

Modifier

Modifier在Gameplay Effect目录下,作用是修改Attribute,一个Modifier对应一个Attribute

Attribute:要修改的Attribute,AttributeSetBase是自己写的C++ AS类。

Modifier Op:运算符(Operator),有加、乘、除和覆盖四种。

Modifier Magnitude:运算值,与Attribute进行Modifier Op选择的运算。

Tags:是否能修改该Attribute的限制条件,这里指ASC组件上的Tag。

我们制作GE,主要任务就是确定AttributeOpMagnitude,而Magnitude是最灵活的一部分可以通过四种方式得到,下面即将介绍。

Magnitude的计算
Magnitude的计算方式有四种,对应四种Magnitude Calculation Type:

  • Scalable Float:不计算,直接给定一个浮点数作为Magnitude的值,也可以从等级曲线中获得。要注意的是如果使用了曲线图表,图表里获得的值会和输入的数相乘。
  • Attribute Based:读取玩家或目标属性作为一个值,可以进行简单线性计算。

主要分为三个部分,上面的部分是运算系数,公式为:

要修改的Attribute和用来计算Magnitude的Attribute是不一样的,为了区分这里称后者为Attr。Coe,Pre,Post都可以通过等级图表获得。中间的部分为Attr的来源,图例为目标的生命值。可以实现如偷取敌方最大生命值20%的效果。AttributeCurve的存在意义不是很清楚。

根据《GameplayEffect(一)功能》的说明,正确的公式应该如下,有待测试:

关于Snapshot,官方文档是这么说明的:

快照(Snapshot)意味着取GameplayEffectSpec被创建时属性的值,否则取GameplayEffectSpec被应用时属性的值。

正常情况下,我们为角色应用GE需要调用ApplyGEToOwner节点(详见5.4.1),此时系统会自动帮我们创建一个Spec实例并将其Apply。

但我们也可以手动地调用MakeOutgoingGESpec节点来实例化一个GE,修改其值后,再使用ApplyGESpecToOwner节点应用该Spec(该方法在3.5.2有使用)。

如果想要获取修改前的值,就可以勾选Snapshot。

下面的Attribute Calculation Type有三种,代表Attr使用的值:

  • Attribute Magnitude:使用Current Value。
  • Attribute Base Value:字面意思。
  • Attribute Bonus Magnitude:使用Current Value - Base Value。

Tag也是计算限制条件,不过是对Attr的限制,而不是上文的Attribute。

  • Custom Calculation Class:自定义的更复杂的运算规则,与AttributeBase相比好处是可以获取任意数量的Attr。

点击Calculation Class旁边的加号,将创建一个GameplayModMagnitudeCalculation蓝图类。

里面唯一的重载函数就是写计算逻辑的地方,返回的float就是Magnitude值。

此外还有一个继承的变量RelevantAttributeToCapture,可以在类默认值设置要Capture的Attribute及其来源。

但这个蓝图应该只是半成品,Spec和GE Attribute Capture Definition结构体都没法拆分,想要在蓝图使用还需要去C++部分自己实现一些函数给蓝图。

如果使用C++的方式编写MMC,可以参考这篇文章:
《虚幻四Gameplay Ability System入门(7)-Gameplay Effect详解(2)自定义Calculation Class》

对照着上图理解会更直观一些。同时在3.5.2处设置Cost时也有一个使用MMC的例子。

  • Set By Caller:通过蓝图获得Magnitude。

一般情况下,我们Apply一个GE后,系统会自动帮我们生成一个GE的Spec并添加到目标的ASC上。5.4会说明GE的一般使用方法。

这里的思路不太一样,我们先是创建了一个GE的实例Spec,用Caller修改指定Modifier的Magnitude之后,再将Spec Apply到目标上

Data Tag则用于区分多个Modifier,告诉蓝图修改哪个Modifier的Magnitude,建议用Data开头。

比较简单,也非常好用。配置完之后可以从GA或是ASC按照下图所示方法使用该GE。

图例为创建一个Cooldown GE的实例后,再将Cooldown值赋给对应Data的Magnitude。Cooldown的实现并不是这样,这里只是一个演示,可以自行换成其他属性。

Execution 

更高级的Modifier,一个Execution就能设置多个Attribute。

和上面计算Modifier的Magnitude用的CalculationClass类似,区别在于上文用到的MagnitudeCalculation是获取多个Attr以计算Magnitude,再通过Magnitude修改Attribute

而这个ExecutionCalculation是直接获取多个Attribute进行修改

此外,Conditional GE可以设置Execution成功执行时候Apply的GE,这也是非主动应用GE的方法之一。

图的最下面也有一个Conditional GE,注意二者是不一样的。鼠标悬停也能查看二者的差别。

GE的应用

GE的应用我们称为Apply,可以从GA或者ASC去Apply一个GE。

GE可以通过蓝图手动应用,也可以通过GE的配置项,使GE在特定条件下嵌套应用其他GE。

GE的主动应用

应用GE的时候,我们可以设置GE的等级。

Stacks表示应用多少层的GE,仅在GA里Apply GE时才能设置此项

注意重复调用该节点也算多层GE叠加,Stack详见5.2.4。

GA中Apply GE的例子

ASC中Apply GE的例子

Apply的对象有Owner也有Target,Owner比较省事。

如果想让敌人扣血,可以在GA里先Send一个Gameplay Event,通过Event调用Target的播放受击动画的GA,再在GA里Apply一个扣血GE To Self。

GE的嵌套应用

涉及3个配置项,对应不同的条件:

  • Gameplay Effect:当前GE成功应用后,应用配置好的GE。
  • Overflow:GE层数溢出时,应用配置好的GE,可以做满层爆炸的效果。
  • Expiration:GE中断或结束时,应用配置好的GE。

Gameplay Cue 

GameplayCues(GC)执行非游戏性相关的事情,比如音效,粒子特效,震屏等。

GameplayCues通常会被复制和预测(除非设置Executed, Added或Removed是本地的)。

主要有Static和Actor两类GC。

Static适用于单次播放的特效。由于其是静态的,不会产生实例,因此在其蓝图里创建的变量都是只读的。对应Instant和Periodic的GE。

Actor适用于持久的,不定时长的特效。其继承自一个场景Actor,每次使用会产生一个对应实例。对应Infinity和Has Duration的GE。

GC的制作

打开窗口-GameplayCue编辑器,可以看到如下页面:

每一个GC(处理器/Handler)需要一个对应的Tag,点击新增会显示GC蓝图创建页面。

根据需求选择即可。

不用GameplayCue编辑器,直接创建GC蓝图也是可以的,但是记得在类默认值中设置Tag。

 

Static类型GC设置

对于Static的GC,仅需重载OnExecute函数即可。

通过获取传入参数Target的根组件,就可以附加粒子系统发射器了。

Parameter用于传入一些参数,如伤害飘字的数值等。

Actor类型GC设置

Actor类型GC继承自场景Actor,因此有Tick、BeginPlay、Overlap等其他函数。

因此其类默认值也有很多设置,最主要的是Gameplay Cue和CleanUp两个目录。

这里我们重载OnActive和OnRemove两个函数即可。

和Static类型的GC不同的是,如果我们勾选了Gameplay Cue的Auto Attach GC To Owner,我们可以用GC自身的根组件作为发射器要附加的组件(在不需要绑定到指定骨骼的情况下)。

此外,由于Actor类型的GC是非静态的,可以产生实例,因此是可以创建变量并写入的。

GC的调用 

一般通过GE配置,也可以在GA里调用Execute/Add触发。

从GE配置GC

选择GC对应的Tag即可,可以同时选择多个Tag,触发多个GC。

Require Modifier Success to Trigger Cues:需要GE成功修改Attribute后才调用GC,而不仅仅是Apply该GE。

Suppress Stacking Cues:多个GE存在Stack中时是否实例化多个GC(如果使用了Stack,对应的一定是可以实例化的Actor类GC)。

Min、Max Level和Magnitude Attribute则与传入参数有关。

Raw Magnitude即为Magnitude Attribute的值,而Normalized Magnitude的计算方式如下:
$Normalized = (Raw - Min) / (Max - Min)$

如上图所示,当Min=0且Max=100时,Normalized = Raw / 100,即百分比。

从GA调用GC

共有五个相关函数,Add&Remove与Execute分别对应Actor类型和Static类型的GC,具体用法见图。

 总结

整个GAS系统的工作流程如图所示。

ASC管理GA、GE、Attribute。

GE可以用来给予ASC一个GA,也可以修改Attribute。(甚至还能Apply其他的GE,图中没有提到。)

GA可以发送Event给其他ASC,调用对应的GA;也可以对目标Apply一个GE,修改其属性。

GE和GA都可以用来触发GC。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/392441.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客周赛 Round 54 (A~E)

#牛客周赛 Round 54 &#xff08;A~E&#xff09; 前言&#xff1a; 以后会定时更新很多比赛的题解 希望借此让自己坚持赛后补题 要不然写完就结束 自己水平没有一点提高 本人很菜所以不会更新 太难的题 加油&#xff01;&#xff01;&#xff01;1. ​清楚姐姐的糖葫芦…

C语言之递归函数

文章目录 &#x1f34a;自我介绍&#x1f34a;递归函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&#xff0c;我是小珑也要变强&#xff08;也是小珑&…

C#学习笔记12:SYN6288语音模块_Winform上位机控制软件

今日尝试使用C# Winform写一个上位机软件控制 SYN6288语音模块 这里不讲什么基本原理(或者讲的比较略简)&#xff0c;直接讲实现了就...... 文章提供测试代码讲解、测试效果图、整体测试工程下载 目录 控件的摆放&#xff1a; SYN6288介绍: 代码编程&#xff1a; 对16进制发送…

VUE.js

目录 一、什么是VUE.js 二、VUE.js优点 三、VUE安装 四、第一个VUE程序 五、Vue指令 v-text v-html v-on v-model v-show v-if v-bind v-for 六、VUE实例生命周期 七、Vue-CLI搭建项目 主要的功能 需要的环境 八、组件路由 搭建步骤: 1. 创建 router 目录 …

ctfhub 命令注入

知识点 1.常见的拼接符 1、A ; B 先执行A&#xff0c;再执行B 2、A & B 简单的拼接 3、A | B 显示B的执行结果 4、A&&B A执行成功之后才会执行B 5、A || B A执行失败之后才会执行B , 在特殊情况下可代替空格 2.常见的命令 &#…

CentOS7 VMware虚拟机基于NAT配置网络IP

目录 前言 VMnet8 虚拟网络编辑 ens33 ping 防火墙 前言 平时学习时一直需要用到Linux服务器&#xff0c;一般都是在Windows上安装VMware来创建一个虚拟机。创建的虚拟机需要配置网络才能够访问外网&#xff0c;可以通过以下两种方式来配置虚拟机网络 桥接模式NAT模式&…

「测试线排查的一些经验-上篇」 后端工程师

文章目录 端口占用脚本失灵线上部署项目结构模版配置文件生效 一般产品研发过程所使用的环境可分为&#xff1a; 研发环境-dev测试环境-test生产环境-prod 软件开发中&#xff0c;完整测试环境包括&#xff1a;UT、IT、ST、UAT UT Unit Test 单元测试 IT System Integration …

MoE-LLaVA: Mixture of Experts for Large Vision-Language Models

发表时间&#xff1a;6 Jul 2024 论文链接&#xff1a;https://arxiv.org/pdf/2401.15947 作者单位&#xff1a;Peking University Motivation&#xff1a;最近的进展表明&#xff0c;扩展大型视觉语言模型 (LVLM) 有效地提高了下游任务的性能。然而&#xff0c;现有的缩放方…

深度学习笔记(神经网络+VGG+ResNet)

深度学习 主要参考博客常用英语单词 概念应用神经网络基础神经网络基本结构 超参数超参数是什么常用超参数超参数搜索过程常用超参数调优办法&#xff08;通过问题复杂度和计算资源选择&#xff09; 激活函数介绍为什么要使用激活函数推荐博客 sigmoid激活函数&#xff08;使用…

第R1周:RNN-心脏病预测

本文为&#x1f517;365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊 要求&#xff1a; 1.本地读取并加载数据。 2.了解循环神经网络&#xff08;RNN&#xff09;的构建过程 3.测试集accuracy到达87% 拔高&#xff1a; 1.测试集accuracy到达89% 我的环境&a…

[windows10]win10永久禁用系统自动更新操作方法

WinR打开运行 输入regedit打开注册表 点击确定打开注册表 按照如下路径找到UX 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 在空白处点击鼠标右键&#xff0c;新建选择DWORD&#xff0c;然后重命名为FlightSettingsMaxPauseDays 双击FlightSet…

Vue项目学习(项目的开发流程)(2)

1、vue项目的默认首页和入口文件 2、两种书写的方式是表达一样的意思——>el&#xff1a;指定当前Vue实例挂载到哪个区域里 3、如果属性值和属性名一致&#xff0c;冒号和后面可以省略不写 &#xff08;所以有两种写法&#xff09; 4、以".vue"文件结尾的文件在项…

Linux操作系统之进程信号

进程信号 一、信号1、概念2、系统定义的信号列表3、常见的信号处理方式 二、产生信号的方式1、终端按键&#xff08;1&#xff09;组合键&#xff08;2&#xff09;示例代码&#xff08;3&#xff09;运行结果 2、调用系统函数&#xff08;1&#xff09;kill命令&#xff08;2&…

CRC16循环冗余校验

代码&#xff1a; #include<stdio.h> #include <stdint.h>#define uchar unsigned char #define uint unsigned int static const uint8_t auchCRCHi[] { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x0…

深入理解接口测试:实用指南与最佳实践(三)API文档解析及编写测试用例

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 这一阶段是接口测试的学习&#xff0c;我们接下来的讲解都是使用Postman这款工具&#xff0c;当然呢Postman是现在一款非常流行的接口调试工具&#xff0c;它使用简单&#xff0c;而且功能也很强大。不仅测试人员会使用…

pxe自动安装linux

实验环境 1.rhel7主机 2开启主机图形&#xff08;本人最小化安装&#xff0c;先下载&#xff09; 3配置网络 4关闭VMware dhcp功能 5能够自动安装系统 完成rhedhat7图形,kickstart,启动图形化制作工具 安装kickstart 启动图形化制作工具 在ks.cfg可以添加安装时下载的包 …

算法学习day29

一、乘法表中第k小的数(和有序矩阵中第k小的数类似) 题意&#xff1a; 乘法表是大小为 m x n 的一个整数矩阵&#xff0c;其中 mat[i][j] i * j&#xff08;下标从 1 开始&#xff09;。 给你三个整数 m、n 和 k&#xff0c;请你在大小为 m x n 的乘法表中&#xff0c;找出…

可视化图表与源代码显示的动态调整

可视化图表与源代码显示的动态调整 页面效果描述&#xff1a;本篇代码实现了通过拖动一个可调整大小的分隔符&#xff0c;用户可以动态地调整图表显示区域和源代码显示区域的大小。通过监听鼠标事件&#xff0c;当用户拖动分隔符时&#xff0c;会动态计算并更新两个区域的大小 …

Vue项目学习(1)

1、进入cmd命令行——> vue ui ——>等等操作 2、 3、src目录下 4、vue项目的启动 &#xff08;1&#xff09; &#xff08;2&#xff09; 5、如何更改前端vue项目的端口号&#xff1f;——>去vue.config.js里配置应一个对象

mprpc框架的应用示例

一、注册 有一个本地服务&#xff0c;我想把它发布成远程服务&#xff0c;首先在user.proto中定义rpc方法的描述&#xff0c;定义参数和响应的消息类型 然后在userservice.cc文件中通过继承UserServiceRpc这个类&#xff0c;重写一下响应的方法&#xff08;打四个动作&#xf…