UE 我的GAS文档

一些说明

GamePlay Ability 一般称为技能。缩写GA
GamePlay Effect 一般称为效果,其他技能系统可能直接就叫buff了,所需一般叫GE

https://gitee.com/weilin3101/teampleta-gas

如何用GAS注册并释放一个技能

创建一个Ability

UE5.2.1
必要插件:

体验优化插件,蓝图格式化,蓝图截屏(免费)

build.cs如果没有记得加上"GameplayAbilities", "GameplayTags", "GameplayTasks"

右键创建技能


添加GamePlayTag。相当于一个字符串类型的技能ID。

也可以在项目设置->gameplay tag 这里添加

然后我们创建一个Actor

需要一个static mesh 随便拿一个模型表示一下,关掉碰撞
那一个box collision,碰撞只查询,其他以后弄
加一个projectile movement component。让box设置一下初速度,其他随便比如重力设置。
然后开始打开GA_FireBox 开始写技能。这个技能在角色前方向生成一个Box,1s后技能结束

注册技能

打开角色character的蓝图。我们需要给角色加上这个组件


然后注册技能并释放。及其三种释放技能的方式

注册并马上释放仅仅执行一次

第二次想再拿这个handle 激活是无效的。可以用于做一些低频行为。这个技能是不是角色的技能,是一个采集行为这种不需要一直挂在角色身上。

GameplayAbility_Montage


继承自 GameplayAbility。他的Tag“CWL.Skill.FireBoxEx”

我这里还是第三人称模板包,所以我们随便创建一个跳跃蒙太奇丢这里


技能里面只延迟

角色蓝图直接快速释放

效果按下1,角色跳了一下。同理可以换成攻击动画。

创建蓝图,基础自GameplayEffect。叫GE_Damage。
Effect可以理解为buff,或者事件回调的概念。
我们这个技能效果可能是打出了xx伤害,播放了一个特效,给谁加了防御等等。


上面的GameplayAbility_Montage 刚刚好有一个加Effect的地方。
这个Effect可以,直接编辑角色的的属性(到这里还没讲GAS的属性)比如扣血20等

其他的可以直接去看源码,激活技能的时候 ApplyGameplayEffectToSelf 触发effect。蒙太奇结束的时候移除。

GameplayAbility_Jump

创建一个 Jump技能, 并改一下箭头位置,不改会警告然后再让你改。

直接释放

基本就是官方给出的学习模板了

// Copyright Epic Games, Inc. All Rights Reserved.

#include "Abilities/GameplayAbility_CharacterJump.h"
#include "GameFramework/Character.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(GameplayAbility_CharacterJump)

// --------------------------------------------------------------------------------------------------------------------------------------------------------
//
//  UGameplayAbility_CharacterJump
//
// --------------------------------------------------------------------------------------------------------------------------------------------------------

UGameplayAbility_CharacterJump::UGameplayAbility_CharacterJump(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
    InstancingPolicy = EGameplayAbilityInstancingPolicy::NonInstanced;
}

void UGameplayAbility_CharacterJump::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{

    if (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo))
    {
        if (!CommitAbility(Handle, ActorInfo, ActivationInfo))
        {
            return;
        }

        ACharacter * Character = CastChecked<ACharacter>(ActorInfo->AvatarActor.Get());
        Character->Jump();
    }
}

void UGameplayAbility_CharacterJump::InputReleased(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo)
{
    if (ActorInfo != NULL && ActorInfo->AvatarActor != NULL)
    {
        CancelAbility(Handle, ActorInfo, ActivationInfo, true);
    }
}

bool UGameplayAbility_CharacterJump::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const
{
    if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags))
    {
        return false;
    }

    const ACharacter* Character = CastChecked<ACharacter>(ActorInfo->AvatarActor.Get(), ECastCheckedType::NullAllowed);
    return (Character && Character->CanJump());
}

/**
 *  Canceling an non instanced ability is tricky. Right now this works for Jump since there is nothing that can go wrong by calling
 *  StopJumping() if you aren't already jumping. If we had a montage playing non instanced ability, it would need to make sure the
 *  Montage that *it* played was still playing, and if so, to cancel it. If this is something we need to support, we may need some
 *  light weight data structure to represent 'non intanced abilities in action' with a way to cancel/end them.
 */
void UGameplayAbility_CharacterJump::CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility)
{
    if (ScopeLockCount > 0)
    {
        WaitingToExecute.Add(FPostLockDelegate::CreateUObject(this, &UGameplayAbility_CharacterJump::CancelAbility, Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility));
        return;
    }

    Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility);

    ACharacter * Character = CastChecked<ACharacter>(ActorInfo->AvatarActor.Get());
    Character->StopJumping();
}

FGameplayAbilityActorInfo

在Ability可以调用GetActorInfo() 获取谁释放的技能。及其其他信息。在1.1 中我们从中获取了施法者Actor。

/**
 *  FGameplayAbilityActorInfo
 *
 *  Cached data associated with an Actor using an Ability.
 *      -Initialized from an AActor* in InitFromActor
 *      -Abilities use this to know what to actor upon. E.g., instead of being coupled to a specific actor class.
 *      -These are generally passed around as pointers to support polymorphism.
 *      -Projects can override UAbilitySystemGlobals::AllocAbilityActorInfo to override the default struct type that is created.
 *
 */
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayAbilityActorInfo
{
    GENERATED_USTRUCT_BODY()

    virtual ~FGameplayAbilityActorInfo() {}

    /** The actor that owns the abilities, shouldn't be null */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    TWeakObjectPtr<AActor>    OwnerActor;

    /** The physical representation of the owner, used for targeting and animation. This will often be null! */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    TWeakObjectPtr<AActor>    AvatarActor;

    /** PlayerController associated with the owning actor. This will often be null! */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    TWeakObjectPtr<APlayerController> PlayerController;

    /** Ability System component associated with the owner actor, shouldn't be null */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    TWeakObjectPtr<UAbilitySystemComponent>   AbilitySystemComponent;

    /** Skeletal mesh of the avatar actor. Often null */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    TWeakObjectPtr<USkeletalMeshComponent>    SkeletalMeshComponent;

    /** Anim instance of the avatar actor. Often null */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    TWeakObjectPtr<UAnimInstance> AnimInstance;

    /** Movement component of the avatar actor. Often null */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    TWeakObjectPtr<UMovementComponent>    MovementComponent;

    /** The linked Anim Instance that this component will play montages in. Use NAME_None for the main anim instance. */
    UPROPERTY(BlueprintReadOnly, Category = "ActorInfo")
    FName AffectedAnimInstanceTag; 

    /** Accessor to get the affected anim instance from the SkeletalMeshComponent */
    UAnimInstance* GetAnimInstance() const;

    /** Returns true if this actor is locally controlled. Only true for players on the client that owns them (differs from APawn::IsLocallyControlled which requires a Controller) */
    bool IsLocallyControlled() const;

    /** Returns true if this actor has a PlayerController that is locally controlled. */
    bool IsLocallyControlledPlayer() const;

    /** Returns true if the owning actor has net authority */
    bool IsNetAuthority() const;

    /** Initializes the info from an owning actor. Will set both owner and avatar */
    virtual void InitFromActor(AActor *OwnerActor, AActor *AvatarActor, UAbilitySystemComponent* InAbilitySystemComponent);

    /** Sets a new avatar actor, keeps same owner and ability system component */
    virtual void SetAvatarActor(AActor *AvatarActor);

    /** Clears out any actor info, both owner and avatar */
    virtual void ClearActorInfo();
};

如何造成伤害 & Attribute

到这里就不能纯蓝图了,部分换C++
如果报错了检查这里。build.cs记得加上"GameplayAbilities", "GameplayTags", "GameplayTasks"
由于某种原因项目炸了,下面的部分是我又新建了一个项目。

新建项目:
这是一个技能。Tag 是 WL.Skill.FireBox


注意这里Spawn Actor的Instigator需要填,以便于生成的碰撞体盒子,能够获取谁生成的。
然后是这个盒子,通过Static Mesh 作为碰撞查询,Projectile 用于发射。

盒子打到人的时候我们打印一个 -10

然后按键1发射技能

大概是这个效果,发射一个蓝Box,击中打印-10

然后我们把打印-10改掉。
我们把打印-10改成了对目标照成一个效果。只是这个效果是扣血。

这个效果叫GameplayEffect。左右在Attribute上。我们先需要创建一个Attribute类。

Attribute值的是角色属性,生命,物理攻击,魔法攻击等数值,我们自己用变量维护当然可以。
接入虚幻的优势在于,我们可以创建GamplayEffect,并在编辑器中直接编辑属性。

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "T_GAS/DataTable/WCharacterDataTable.h"
#include "WAttributeSet.generated.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 T_GAS_API UWAttributeSet : public UAttributeSet
{
    GENERATED_BODY()
public:
    virtual bool PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data) override;
    virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

    UFUNCTION(BlueprintCallable, Category="Attribute")
    virtual void RegisterProperties(const FWCharacterDataTable& InData);
protected:
    static void RegisterParam(FGameplayAttributeData& InAttributeData, const float InValue);

    UFUNCTION()
    virtual void OnRep_Level(const FGameplayAttributeData& OldLevel);

    UFUNCTION()
    virtual void OnRep_PhysicsAttack(const FGameplayAttributeData& OldPhysicsAttack);

    UFUNCTION()
    virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);

    UFUNCTION()
    virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);

    UFUNCTION()
    virtual void OnRep_Damage(const FGameplayAttributeData& OldDamage);
public:
    UPROPERTY(BlueprintReadOnly, Category="Attribute", ReplicatedUsing=OnRep_Level)
    FGameplayAttributeData Level;
    ATTRIBUTE_ACCESSORS(UWAttributeSet, Level)

    UPROPERTY(BlueprintReadOnly, Category="Attribute", ReplicatedUsing=OnRep_PhysicsAttack)
    FGameplayAttributeData PhysicsAttack;
    ATTRIBUTE_ACCESSORS(UWAttributeSet, PhysicsAttack)

    UPROPERTY(BlueprintReadOnly, Category="Attribute", ReplicatedUsing=OnRep_MaxHealth)
    FGameplayAttributeData MaxHealth;
    ATTRIBUTE_ACCESSORS(UWAttributeSet, MaxHealth)

    UPROPERTY(BlueprintReadOnly, Category="Attribute", ReplicatedUsing=OnRep_Health)
    FGameplayAttributeData Health;
    ATTRIBUTE_ACCESSORS(UWAttributeSet, Health)

    UPROPERTY(BlueprintReadOnly, Category="Attribute", ReplicatedUsing=OnRep_Damage)
    FGameplayAttributeData Damage;
    ATTRIBUTE_ACCESSORS(UWAttributeSet, Damage)
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "WAttributeSet.h"
#include "GameplayEffectExtension.h"
#include "Net/UnrealNetwork.h"

bool UWAttributeSet::PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data)
{
    return Super::PreGameplayEffectExecute(Data);
}

void UWAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);

    if (Data.EvaluatedData.Attribute == GetDamageAttribute())
    {
        const float Value = GetDamage();
        SetHealth(FMath::Clamp(GetHealth() - Value, 0.0f, GetMaxHealth()));
    }
}

void UWAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    DOREPLIFETIME(UWAttributeSet, Level);
    DOREPLIFETIME(UWAttributeSet, PhysicsAttack);
    DOREPLIFETIME(UWAttributeSet, MaxHealth);
    DOREPLIFETIME(UWAttributeSet, Health);
    DOREPLIFETIME(UWAttributeSet, Damage);
}

void UWAttributeSet::RegisterProperties(const FWCharacterDataTable& InData)
{
    RegisterParam(Level, 1);
    RegisterParam(Health, InData.Health);
    RegisterParam(MaxHealth, InData.Health);
    RegisterParam(PhysicsAttack, InData.PhysicsAttack);
}

void UWAttributeSet::RegisterParam(FGameplayAttributeData& InAttributeData, const float InValue)
{
    InAttributeData.SetBaseValue(InValue);
    InAttributeData.SetCurrentValue(InValue);
}

void UWAttributeSet::OnRep_Level(const FGameplayAttributeData& OldLevel)
{
    GAMEPLAYATTRIBUTE_REPNOTIFY(UWAttributeSet, Level, OldLevel);
}

void UWAttributeSet::OnRep_PhysicsAttack(const FGameplayAttributeData& OldPhysicsAttack)
{
    GAMEPLAYATTRIBUTE_REPNOTIFY(UWAttributeSet, PhysicsAttack, OldPhysicsAttack);
}

void UWAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth)
{
    GAMEPLAYATTRIBUTE_REPNOTIFY(UWAttributeSet, MaxHealth, OldMaxHealth);
}

void UWAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
    GAMEPLAYATTRIBUTE_REPNOTIFY(UWAttributeSet, Health, OldHealth);
}

void UWAttributeSet::OnRep_Damage(const FGameplayAttributeData& OldDamage)
{
    GAMEPLAYATTRIBUTE_REPNOTIFY(UWAttributeSet, Damage, OldDamage);
}

同时我们还有一张配置表,在BeginPlay的时候读取并设置角色的属性值。比较简单,不展开

void UWAttributeSet::RegisterProperties(const FWCharacterDataTable& InData)
{
    RegisterParam(Level, 1);
    RegisterParam(Health, InData.Health);
    RegisterParam(MaxHealth, InData.Health);
    RegisterParam(PhysicsAttack, InData.PhysicsAttack);
}

void UWAttributeSet::RegisterParam(FGameplayAttributeData& InAttributeData, const float InValue)
{
    InAttributeData.SetBaseValue(InValue);
    InAttributeData.SetCurrentValue(InValue);
}

然后需要一个角色血条

UI代码随便写一下

来看我们创建的GameplayEffect。效果是对属性Damage进行Add 10的操作。上文我们apply 一个 effect to target 的时候,我们填了一个effect就是这个。

然后代码会跑到这里,更新血量,然后UI实时更新数据。

void UWAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);

    if (Data.EvaluatedData.Attribute == GetDamageAttribute())
    {
        const float Value = GetDamage();
        SetHealth(FMath::Clamp(GetHealth() - Value, 0.0f, GetMaxHealth()));
    }
}

到此我们实现了一个释放技能并把对方打扣血的一个流程。

AbilityTask_WaitGameplayEvent

上面的代码有一点不好,apply的代码写在了碰撞这边。
所以我们Ability里面改成这样。这里有一个wait event


碰撞检测那边改成send event

本质是这个 UAbilityTask_WaitGameplayEvent

PS: 上面是测试逻辑,实际上如果攻击打空了,碰撞体销毁了,也需要EndAbility。


在技能wait击中事件的地方后,wait销毁事件

技能消耗

属性里面加上魔法值属性

    UPROPERTY(BlueprintReadOnly, Category="Attribute", ReplicatedUsing=OnRep_MaxMana)
    FGameplayAttributeData MaxMana;
    ATTRIBUTE_ACCESSORS(UWAttributeSet, MaxMana)

    UPROPERTY(BlueprintReadOnly, Category="Attribute", ReplicatedUsing=OnRep_Mana)
    FGameplayAttributeData Mana;
    ATTRIBUTE_ACCESSORS(UWAttributeSet, Mana)

然后简单做个蓝条

在技能的右下角有一个消耗的 Gameplay Effect。


我们创建这个effect,并写上mana扣10

记得在技能的开头提交技能,CommitAbility内部会进行cost是否成功的计算。

冷却时间


在屏幕右小角创建一个CD文本。

创建一个GE,区间,4s


并且这里有一个标签。

ability配置里面有一个CD,填上

受击效果 GameplayCue


创建标签,并添加处理器。我这里创建的是static的处理器。

然后在effect里面这样配置,当damage触发的时候,触发这个cue。这里标签,定位到了刚刚我们创建的Cue。

然后我们在Hit里面重写接口,创建一个特效

数值计算 ExecutionCalculation

void UWAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);

    if (Data.EvaluatedData.Attribute == GetDamageAttribute())
    {
        const float Value = GetDamage();
        SetHealth(FMath::Clamp(GetHealth() - Value, 0.0f, GetMaxHealth()));
    }
}

我们每一个属性变更都需要在这里写代码,GAS支持把这一部分独立为一个单独的对象。

在GE的这里,可以填很多个 Executions

这里类我们用C++写

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameplayEffectExecutionCalculation.h"
#include "WExecCalDamage.generated.h"

/**
 * 
 */
UCLASS()
class T_GAS_API UWExecCalDamage : public UGameplayEffectExecutionCalculation
{
    GENERATED_BODY()
public:
    UWExecCalDamage();
    virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override;
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "WExecCalDamage.h"
#include "T_GAS/Ability/Attribute/WAttributeSet.h"

struct FExecCalDamageStatics
{
    DECLARE_ATTRIBUTE_CAPTUREDEF(PhysicsAttack);
    DECLARE_ATTRIBUTE_CAPTUREDEF(Health);
    DECLARE_ATTRIBUTE_CAPTUREDEF(Damage);

    FExecCalDamageStatics()
    {
        DEFINE_ATTRIBUTE_CAPTUREDEF(UWAttributeSet, PhysicsAttack, Source, false);
        DEFINE_ATTRIBUTE_CAPTUREDEF(UWAttributeSet, Damage, Source, true);
        DEFINE_ATTRIBUTE_CAPTUREDEF(UWAttributeSet, Health, Target, true);
    }
};

UWExecCalDamage::UWExecCalDamage()
{
    FExecCalDamageStatics ExecCalDamage;
    RelevantAttributesToCapture.Add(ExecCalDamage.PhysicsAttackDef);
    RelevantAttributesToCapture.Add(ExecCalDamage.DamageDef);
    RelevantAttributesToCapture.Add(ExecCalDamage.HealthDef);

    // InvalidScopedModifierAttributes.Add(ExecCalDamage.PhysicsAttackDef);
    // InvalidScopedModifierAttributes.Add(ExecCalDamage.DamageDef);
    InvalidScopedModifierAttributes.Add(ExecCalDamage.HealthDef);

}

void UWExecCalDamage::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams,
    FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
    // 执行顺序在 PostGameplayEffectExecute 后

    FExecCalDamageStatics ExecCalDamage;

    UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();
    UAbilitySystemComponent* SourceASC = ExecutionParams.GetSourceAbilitySystemComponent();

    const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
    const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
    const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

    FAggregatorEvaluateParameters EvaluationParameters;
    EvaluationParameters.SourceTags = SourceTags;
    EvaluationParameters.TargetTags = TargetTags;

    float Health = 0.0f;
    float PhysicsAttack = 0.0f;
    float Damage = 0.0f;

    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(ExecCalDamage.HealthDef, EvaluationParameters, Health);
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(ExecCalDamage.PhysicsAttackDef, EvaluationParameters, PhysicsAttack);
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(ExecCalDamage.DamageDef, EvaluationParameters, Damage);

    float FinalDamage = Damage + PhysicsAttack;
    FinalDamage = FMath::Clamp(FinalDamage, 0.0f, Health);
    OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(ExecCalDamage.HealthProperty, EGameplayModOp::Additive, -FinalDamage));
}

可以注意到 ExecutionCalculation 可以加很多个,可以支持其他效果,比如卡肉。
比如

void UExecCalKa::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams,
                                        FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
    UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();
    UAbilitySystemComponent* SourceASC = ExecutionParams.GetSourceAbilitySystemComponent();

    if (AT_GASCharacter* InCharacter = Cast<AT_GASCharacter>(SourceASC->GetOwner()))
    {
        InCharacter->DelayFrame(0.1f);
    }
}

然后在角色身上用异步触发一下pause true 和 false

void AT_GASCharacter::DelayFrame(const float InTime)
{
    if (APlayerController* InPlayerController = Cast<APlayerController>(GetController()))
    {
        InPlayerController->SetPause(true);
    }
}

Condition GE


effect可以再次触发effect。
比如一个技能打到怪,又触发了一次二次伤害。
下面可以填一个tag。
比如触发这个damage的tag的 skill.fire。并且这个二次effect的tag也是这个。那么就可以连锁触发。否则就不会连锁触发。

技能释放的条件和限制

(支持前缀匹配,比如TAG是,WL.Skill 那么 WL.Skill.A, WL.SKill.B 都生效)

  • Cancel Ability With Tag
    • 释放当前技能的时候会,取消这个tag的释放
  • Block Ability With Tag
    • 释放这个技能的时候,对于tag的技能都不能释放

A技能释放后,才可以释放B技能,才可以释放C技能如何配置

  • Activation Owned Tag
    • 在技能开始到结束之间,可以用这个标签加一个标记。
  • Activation Required Tag
    • 和上面联用,有某个标记的时候才能激活这个技能。
  • Activation Blocked Tag
    • 我是用了A技能,加了一个buff。另一个技能判断拥有这个buff,就不会释放了

      GE Custom Calculation


在damage的这里,我们上面是直接写了一个-10。
其实可以选成一个自定义的计算过程。

这里的数值也可以用Set By Caller

如果这样,技能释放的时候就需要提前设置caller

GE

  • 一次性触发
  • 无限,比如穿上装备的时候增加x属性,脱下的时候去掉
  • 周期性触发

案例:周期时间内回血

创建GE


方便测试,直接把Damage换成了AddHP
并把初始血量调低

打中回血的效果

其中,这里是周期,第一次触发释放执行


第三个比较复杂。有另一个免疫标签。
比如0~10s内的一个持续性伤害。
1.5~5.5s内有了一个免疫buff。这个免疫区间内的伤害是不生效的。过了这个区间后。剩下的周期内扣血如何表现,就是这个选项。具体需要和免疫标签配合

Buff多次叠加


以上面的加血buff为例。
我触发了一个10s的加血buff。2s的时候有被触发了一个10s。
那么:

  • 两个buff是相互独立,各自作用10s?也就是0~10s 加了100血,2s到12s又加了100血,总共加200血。(上面的Stacking Type None)

如果选by Source 和 Target,并且limit不是0,是2


首先Source和 Target 有什么区别呢?

如果1和2,都给3加了两层buff。如果按照target的叠层算。那么数量就是4,超出2。叠加不上去。
如果按Source算,1和2能叠加成功。

  • Stack Duration Fresh Policy: Refresh On Successful Application

Buff A 持续时间0~5s,第3s又叠了一层。那么buff持续事件为 3+5。
如果不勾选刷新,那么第一个buff的5s结束的时候两个buff都会消失。

  • Stack Period Reset Policy: Reset On Successful Application

0~5s触发一个buff。并且在整数秒的时候触发伤害。
在1.5s触发了另一个buff叠加。如果不Reset。那么触发点还是2s,3s。。。
如果reset。那么就是1.5s 重新触发周期。1.5, 2.5 ,3.5 。。。


比如buff叠加了很多层。
第一个buff周期结束的时候,这里buff叠加了很多层。

  • 全部清空
  • 移除单层buff,并刷新周期
  • 刷新周期

Attribute Based

Set By Caller

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇