UE GAS学习

news2024/9/20 10:34:05

【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 IAbilitySystemInterface
 
public:
 
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的等级,这里暂令其全部为1
            AbilitySystemComponent->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;
 
// 根据需要可以设置多种类型Cost
   UPROPERTY(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:
        // Attributes
        UPROPERTY(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.coloradmin.cn/o/1983290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【用C语言编写】题目名称:分数求和题目内容:计算1/1-1/2+1/3-1/4+1/5-......+1/99-1/100的值,打印出结果

题目名称&#xff1a;分数求和 题目内容&#xff1a;计算1/1-1/21/3-1/41/5-......1/99-1/100的值&#xff0c;打印出结果 错误的代码如下&#xff1a; #include <stdio.h> int main() {int i 0;double sum 0.0;int flag 1;for (i 1; i < 100; i){sum flag * 1/ …

OpenTiny HUICharts 正式开源发布,一个简单、易上手的图表组件库

引言 大家好&#xff01; 我们非常高兴地跟大家宣布&#xff0c;今天正式发布我们的新产品——OpenTiny HUICharts。这是一款前端 Web 可视化图表库&#xff0c;其基础图表功能构建于 ECharts 之上&#xff0c;而高阶图表则采用了新的底层技术&#xff0c;以满足更广泛的数据…

JAVA基础:String字符串

目录 前言 String的内部机制 String的两种创建方式 前言 关于String字符串我们并不陌生&#xff0c;在我们的程序中经常使用String这个类&#xff0c;甚至有的同学会把string当成基本数据类型&#xff0c;今天我们就来了解一下String这个类 String的内部机制 我们把string称为…

nature immunology | BACH2调控“调节性”和“促炎性”TH17细胞的染色质多样化状态

–https://doi.org/10.1038/s41590-024-01901-1 BACH2 regulates diversification of regulatory and proinflammatory chromatin states in TH17 cells 留意更多内容&#xff0c;欢迎关注微信公众号&#xff1a;组学之心 研究团队和研究单位 Aviv Regev–Genentech Vijay …

深度学习入门——深度学习

加深网络 前情回顾 构成神经网络的各种层学习时的有效技巧对图像特别有效的CNN参数的最优化方法 向更深的网络出发 网络特点 基于33 的小型滤波器的卷积层。激活函数是ReLU。全连接层的后面使用Dropout层。基于Adam的最优化。使用He初始值作为权重初始值。 进一步提高识别精…

【Linux】yum(工具篇)

文章目录 前言&#xff1a;什么是软件包yum 的介绍yum源yum源的配置第三方源的配置官方源的配置镜像站点安装wget包备份本地yum源配置网易yum源重新生成yum缓存 前言&#xff1a;什么是软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程…

【学术会议征稿】第三届环境遥感与地理信息技术国际学术会议(ERSGIT 2024)

第三届环境遥感与地理信息技术国际学术会议(ERSGIT 2024) 2024 3rd International Conference on Environmental Remote Sensing and Geographic Information Technology&#xff08;ERSGIT 2024&#xff09; 第三届环境遥感与地理信息技术国际学术会议&#xff08;ERSGIT 20…

LVS-DR集群的部署

LVS-DR集群 LVS-DR(Linux Virtual Server Director Server)工作模式&#xff0c;是生产环境中最常用的一种工作模式。 LVS-DR工作原理 LVS-DR 模式&#xff0c;Director Server 作为群集的访问入口&#xff0c;不作为网关使用&#xff0c;节点 DirectorServer 与 Real Serve…

zabbix7.0TLS-01-部署服务端

文章目录 1 介绍1.1 架构1.2 主要概念和名词1.3 最新 7.0 TLS 版本的部分新特性更灵活的资源发现和管理 2 官方部署指导地址3 在 Rocky Linux 9 上安装 zabbix3.1 安装软件包3.2 创建初始化数据库3.3 配置zabbix-server3.4 启动Zabbix server和agent进程3.5 默认监听端口3.6 访…

Linux中Samba服务配置和管理

文章目录 一、Samba介绍1.1、Samba是什么1.2、Samba的核心功能1.3、Samba的主要组件1.4、Samba的工作流程1.5、Samba主要配置文件smb.conf 二、Samba安装2.1、更新yum源2.2、安装Samba客户端和服务器软件包2.3、启动Samba 三、Samba的使用3.1、设置Samba服务的全局选项3.2、tes…

Simulink模型开发中的一些自动化方法

随着Simulink模型的产品化开发进程&#xff0c;许多模型开发人员会关心模型的建模自动化问题。比如如何对模型中的元素进行批量查找和修改&#xff1b;如何构建自己的建模规则对模型进行检查&#xff1b;如何实现测试自动化等。在这些使用场景中我们都需要了解一些Simulink函数…

Puppeteer-py:Python 中的无头浏览器自动化

1. 引言 在当今快速发展的互联网时代&#xff0c;自动化测试和数据抓取变得越来越重要。Puppeteer-py 作为一个 Python 库&#xff0c;提供了一种简单而强大的方法来控制无头浏览器&#xff0c;实现网页的自动化操作。无论是进行端到端的测试&#xff0c;还是抓取动态生成的数…

公司新来的两个Java后端,因题背太熟轻松过面试?

以前面试是背八股文&#xff0c;而2024年的后端面试都是流行问场景题&#xff01;建议大家把面试想简单一点&#xff0c;顺的场景题直接给有需要的人&#xff0c;希望能对大家有所帮助&#xff01; 由于平台篇幅原因&#xff0c;很多java面试资料内容展示不了&#xff0c;需要…

【HarmonyOS NEXT星河版开发学习】小型测试案例03-QQ音乐登录

个人主页→VON 收录专栏→鸿蒙开发小型案例总结​​​​​ 基础语法部分会发布于github 和 gitee上面&#xff08;暂未发布&#xff09; 前言 本案例使用的还是一些基础的语法&#xff0c;主要是知道如何去布局以及分析&#xff0c;Harmony的布局方式也是特别多&#xff0c;没必…

java基础 之 重写equals时为什么要重写hashCode

文章目录 前言回答了解哈希hashCode()总结 前言 了解equals戳这里→java基础 之 equals和的区别 请记住这句话&#xff1a;两个对象相同&#xff0c;哈希码一定相同。哈希码相同&#xff0c;两个对象不一定相同。 回答 只重写equals()方法&#xff0c;不重写hashCode()方法&…

【漏洞复现】致远互联FE协作办公平台 apprvaddNew.jsp SQL注入

文章目录 0x00 漏洞描述影响范围 0x01 测绘工具0x02 漏洞复现0x03 Nuclei检测脚本0x04 修复建议0x05 免责声明 0x00 漏洞描述 致远互联FE协作办公平台是一款为企业提供全方位协同办公解决方案的产品。 在受影响的版本中&#xff0c;攻击者可以未授权访问/witapprovemanage/app…

Animate软件动画类型简介

在Animate软件中&#xff0c;有三种基本的补间动画和一种逐帧动画&#xff0c;这里就简单讲一下这几种动画的概念。 FlashASer&#xff1a;AdobeAnimate2021软件零基础入门教程https://zhuanlan.zhihu.com/p/633230084 FlashASer&#xff1a;实用的各种Adobe Animate软件教程…

Java第一个程序的开发

开发三步骤 编写: a.创建一个文本文档,将后缀名改成.java,变成一个java文件 b.注意:我们需要将文件的后缀名显示出来 编译: a.命令:javac java文件名.java b.注意:javac会将java文件编译,生成一个.class文件(字节码文件),jvm运行只认class文件 运行: a.命令:java class文件名…

开发一个自己的VSCode插件

1、前言 对于一个前端开发者来说&#xff0c;开发工具&#xff0c;最常用的应该就是VSCode了&#xff0c;因为它免费&#xff0c;速度快&#xff0c;提供了丰富了插件等优点&#xff0c;使得越来越多的前端开发者都来使用它了&#xff0c;在开发的时候如果有丰富的插件提供支持…

Spring Cloud微服务项目集成MyBatis

在现代软件开发中&#xff0c;微服务架构已经成为一种流行的解决方案&#xff0c;它能够将应用程序拆分成多个小的、独立的服务。每个服务负责一个特定的业务功能&#xff0c;并可以独立部署和扩展。Spring Cloud是一个提供各种工具和框架以支持微服务开发的开源框架&#xff0…