LyraLog9 GAS1

EqZero 请自动看成Lyra,复刻的时候改了名字,避免逻辑莫名其妙跑起来

UEqZeroAbilitySet

/**
 * UEqZeroAbilitySet
 *
 *      Non-mutable data asset
 */
UCLASS(BlueprintType, Const)
class UEqZeroAbilitySet : public UPrimaryDataAsset
{
    GENERATED_BODY()

public:

    UEqZeroAbilitySet(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

    // Grants the ability set to the specified ability system component.
    // The returned handles can be used later to take away anything that was granted.
    void GiveToAbilitySystem(UEqZeroAbilitySystemComponent* EqZeroASC, FEqZeroAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject = nullptr) const;

protected:

    // GA
    UPROPERTY(EditDefaultsOnly, Category = "Gameplay Abilities", meta=(TitleProperty=Ability))
    TArray<FEqZeroAbilitySet_GameplayAbility> GrantedGameplayAbilities;

    // GE
    UPROPERTY(EditDefaultsOnly, Category = "Gameplay Effects", meta=(TitleProperty=GameplayEffect))
    TArray<FEqZeroAbilitySet_GameplayEffect> GrantedGameplayEffects;

    // AS
    UPROPERTY(EditDefaultsOnly, Category = "Attribute Sets", meta=(TitleProperty=AttributeSet))
    TArray<FEqZeroAbilitySet_AttributeSet> GrantedAttributes;
};

描述一个技能的集合,例如弓箭手就拥有这些技能,打成一个DA。里面的3个结构分别是对GA, GE, AS定义的包装。

是一个类型+相关配置

/**
 * FEqZeroAbilitySet_GameplayAbility
 *
 *      描述一个技能
 */
USTRUCT(BlueprintType)
struct FEqZeroAbilitySet_GameplayAbility
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly)
    TSubclassOf<UEqZeroGameplayAbility> Ability = nullptr;

    UPROPERTY(EditDefaultsOnly)
    int32 AbilityLevel = 1;

    UPROPERTY(EditDefaultsOnly, Meta = (Categories = "InputTag"))
    FGameplayTag InputTag;
};

/**
 * FEqZeroAbilitySet_GameplayEffect
 *
 *      描述一个技能效果
 */
USTRUCT(BlueprintType)
struct FEqZeroAbilitySet_GameplayEffect
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly)
    TSubclassOf<UGameplayEffect> GameplayEffect = nullptr;

    UPROPERTY(EditDefaultsOnly)
    float EffectLevel = 1.0f;
};

/**
 * FEqZeroAbilitySet_AttributeSet
 *
 *      描述一个属性集
 */
USTRUCT(BlueprintType)
struct FEqZeroAbilitySet_AttributeSet
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly)
    TSubclassOf<UAttributeSet> AttributeSet;

};

对于技能集最重要的是这个函数

    /**
     * 将这个AbilitySet授予一个AbilitySystemComponent,并返回授予的技能和效果的Handle
     * @param EqZeroASC 目标AbilitySystemComponent
     * @param OutGrantedHandles 输出参数,包含授予的技能和效果的Handle
     * @param SourceObject 授予来源对象,可选参数,通常用于提供上下文信息(比如哪个物品或角色授予了这个AbilitySet)
     * @return void
     */
    void GiveToAbilitySystem(UEqZeroAbilitySystemComponent* EqZeroASC, FEqZeroAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject = nullptr) const;

在获取装备,设置Pawn Data,通过Game Feature Action 的时候可以通过这个接口添加技能集给角色

例如

遍历PawnData的技能集合加给玩家

void ALyraPlayerState::SetPawnData(const ULyraPawnData* InPawnData)
{
    // 。。。

    for (const ULyraAbilitySet* AbilitySet : PawnData->AbilitySets)
    {
        if (AbilitySet)
        {
            AbilitySet->GiveToAbilitySystem(AbilitySystemComponent, nullptr);
        }
    }

    // 。。。
}

例如武器装备定义,手枪装备的时候就会给角色加上开火,装弹,自动装弹的逻辑。

GE 的作用只看到了PawnData 给角色加了一个 Lyra.Player的标签

AS 没看到实际的作用

void UEqZeroAbilitySet::GiveToAbilitySystem(UEqZeroAbilitySystemComponent* EqZeroASC, FEqZeroAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject) const
{
    // 这个函数就是遍历配置,加GA, GE, AS 没什么好看的
}

添加后会存一下句柄,可能会有删除的需要,例如脱下装备

/**
 * FEqZeroAbilitySet_GrantedHandles
 */
USTRUCT(BlueprintType)
struct FEqZeroAbilitySet_GrantedHandles
{
    GENERATED_BODY()

public:

    void AddAbilitySpecHandle(const FGameplayAbilitySpecHandle& Handle);
    void AddGameplayEffectHandle(const FActiveGameplayEffectHandle& Handle);
    void AddAttributeSet(UAttributeSet* Set);

    void TakeFromAbilitySystem(UEqZeroAbilitySystemComponent* EqZeroASC);

protected:

    UPROPERTY()
    TArray<FGameplayAbilitySpecHandle> AbilitySpecHandles;

    UPROPERTY()
    TArray<FActiveGameplayEffectHandle> GameplayEffectHandles;

    UPROPERTY()
    TArray<TObjectPtr<UAttributeSet>> GrantedAttributeSets;
};

IEqZeroAbilitySourceInterface

// 伤害距离衰减,物理材质来处理爆头伤害的接口
class IEqZeroAbilitySourceInterface
{
    GENERATED_IINTERFACE_BODY()

    /**
     * Compute the multiplier for effect falloff with distance
     *
     * @param Distance          Distance from source to target for ability calculations (distance bullet traveled for a gun, etc...)
     * @param SourceTags        Aggregated Tags from the source
     * @param TargetTags        Aggregated Tags currently on the target
     *
     * @return Multiplier to apply to the base attribute value due to distance
     */
    virtual float GetDistanceAttenuation(float Distance, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const = 0;

    virtual float GetPhysicalMaterialAttenuation(const UPhysicalMaterial* PhysicalMaterial, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const = 0;
};

例如头部的物理材质,影响了爆头伤害,还有距离衰减。

具体我们伤害计算那里看

UEqZeroAbilitySystemComponent

UCLASS(MinimalAPI)
class UEqZeroAbilitySystemComponent : public UAbilitySystemComponent
{
    GENERATED_BODY()

protected:
    UPROPERTY()
    TObjectPtr<UEqZeroAbilityTagRelationshipMapping> TagRelationshipMapping;

    TArray<FGameplayAbilitySpecHandle> InputPressedSpecHandles;
    TArray<FGameplayAbilitySpecHandle> InputReleasedSpecHandles;
    TArray<FGameplayAbilitySpecHandle> InputHeldSpecHandles;

    int32 ActivationGroupCounts[(uint8)EEqZeroAbilityActivationGroup::MAX];
};

看一个类,先看属性

UEqZeroAbilityTagRelationshipMapping

/** 表示不同tag技能的关系,阻塞,取消等 */
USTRUCT()
struct FEqZeroAbilityTagRelationship
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, Category = Ability, meta = (Categories = "Gameplay.Action"))
    FGameplayTag AbilityTag;

    UPROPERTY(EditAnywhere, Category = Ability)
    FGameplayTagContainer AbilityTagsToBlock;

    UPROPERTY(EditAnywhere, Category = Ability)
    FGameplayTagContainer AbilityTagsToCancel;

    UPROPERTY(EditAnywhere, Category = Ability)
    FGameplayTagContainer ActivationRequiredTags;

    UPROPERTY(EditAnywhere, Category = Ability)
    FGameplayTagContainer ActivationBlockedTags;
};

UCLASS()
class UEqZeroAbilityTagRelationshipMapping : public UDataAsset
{
    GENERATED_BODY()

private:
    UPROPERTY(EditAnywhere, Category = Ability, meta=(TitleProperty="AbilityTag"))
    TArray<FEqZeroAbilityTagRelationship> AbilityTagRelationships;
};

UEqZeroAbilityTagRelationshipMapping 是一个DA配置,一组配置,描述技能的关系。例如死亡技能能阻塞其他所有技能。

看看PawnData的配置

/ShooterCore/Game/TagRelationships_ShooterHero.TagRelationships_ShooterHero

例如这里,开火技能,要阻塞表情和装弹的执行,并且要取消标签和换弹技能。

InputSpecHandles

    TArray<FGameplayAbilitySpecHandle> InputPressedSpecHandles;
    TArray<FGameplayAbilitySpecHandle> InputReleasedSpecHandles;
    TArray<FGameplayAbilitySpecHandle> InputHeldSpecHandles;

这三干嘛的

前面我们HeroComp 里面

TArray<uint32> BindHandles;
LyraIC->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles);

把输入Tag转发到了UEqZeroAbilitySystemComponent

void UEqZeroHeroComponent::Input_AbilityInputTagPressed(FGameplayTag InputTag)
{
    // ...
    EqZeroASC->AbilityInputTagPressed(InputTag);
}

void UEqZeroHeroComponent::Input_AbilityInputTagReleased(FGameplayTag InputTag)
{
     // ...
    EqZeroASC->AbilityInputTagReleased(InputTag);
}

例如跳跃技能的输入配置

原生是增强输入是一个InputAction绑定一个回调。

template<class UserClass, typename PressedFuncType, typename ReleasedFuncType>
void UEqZeroInputComponent::BindAbilityActions(const UEqZeroInputConfig* InputConfig, UserClass* Object, PressedFuncType PressedFunc, ReleasedFuncType ReleasedFunc, TArray<uint32>& BindHandles)
{
    // 这里 InputConfig->AbilityInputActions 是一组 InputAction + GameplayTag

    for (const FEqZeroInputAction& Action : InputConfig->AbilityInputActions)
    {
        if (Action.InputAction && Action.InputTag.IsValid())
        {
            if (PressedFunc)
            {
                // 绑定的时候,再回调的时候把技能TAG丢进去
                BindHandles.Add(BindAction(Action.InputAction, ETriggerEvent::Triggered, Object, PressedFunc, Action.InputTag).GetHandle());
            }

            if (ReleasedFunc)
            {
                BindHandles.Add(BindAction(Action.InputAction, ETriggerEvent::Completed, Object, ReleasedFunc, Action.InputTag).GetHandle());
            }
        }
    }
}

InputConfig->AbilityInputActions 是一组 InputAction + GameplayTag

绑定的时候,输入回调的时候把技能TAG丢进去,然后回调到ASC

void ULyraAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& InputTag)
{
    if (InputTag.IsValid())
    {
        for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
        {
            if (AbilitySpec.Ability && (AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InputTag)))
            {
                // 我们有这个技能,按下了,就把技能句柄加到  按下 和 按住 里面
                InputPressedSpecHandles.AddUnique(AbilitySpec.Handle);
                InputHeldSpecHandles.AddUnique(AbilitySpec.Handle);
            }
        }
    }
}

void ULyraAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& InputTag)
{
    if (InputTag.IsValid())
    {
        for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
        {
            if (AbilitySpec.Ability && (AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InputTag)))
            {
                // // 我们有这个技能,松开了,就把技能句柄从 松开 和 按住 里面删除
                InputReleasedSpecHandles.AddUnique(AbilitySpec.Handle);
                InputHeldSpecHandles.Remove(AbilitySpec.Handle);
            }
        }
    }
}

在PlayerController转发过来,相当于输入实现发送后

void AEqZeroPlayerController::PostProcessInput(const float DeltaTime, const bool bGamePaused)
{
    if (UEqZeroAbilitySystemComponent* EqZeroASC = GetEqZeroAbilitySystemComponent())
    {
        EqZeroASC->ProcessAbilityInput(DeltaTime, bGamePaused);
    }

    Super::PostProcessInput(DeltaTime, bGamePaused);
}
void UEqZeroAbilitySystemComponent::ProcessAbilityInput(float DeltaTime, bool bGamePaused)
{
    if (HasMatchingGameplayTag(TAG_Gameplay_AbilityInputBlocked))
    {
        ClearAbilityInput();
        return;
    }

    // 初始化容器,待会我们要激活这些技能
    static TArray<FGameplayAbilitySpecHandle> AbilitiesToActivate;
    AbilitiesToActivate.Reset();

    //
    // 处理所有按住触发的技能
    //
    for (const FGameplayAbilitySpecHandle& SpecHandle : InputHeldSpecHandles)
    {
        if (const FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
        {
            if (AbilitySpec->Ability && !AbilitySpec->IsActive())
            {
                const UEqZeroGameplayAbility* EqZeroAbilityCDO = Cast<UEqZeroGameplayAbility>(AbilitySpec->Ability);
                if (EqZeroAbilityCDO && EqZeroAbilityCDO->GetActivationPolicy() == EEqZeroAbilityActivationPolicy::WhileInputActive)
                {
                    AbilitiesToActivate.AddUnique(AbilitySpec->Handle);
                }
            }
        }
    }

    // ...

准备了一个容器 AbilitiesToActivate 在循环完成后,这里面的技能会被激活

UEqZeroGameplayAbility是我们自己的GA子类。ActivationPolicy是我们自己定义的激活类型,里面有一个按下时候持续触发WhileInputActive

例如 /ShooterCore/Weapons/Shotgun/GA_Weapon_Fire_Shotgun.GA_Weapon_Fire_Shotgun 短枪的开火就是持续激活的。

按住一直射击,如果是触发式的,那么就变成GA的Activity一直触发。

void UEqZeroAbilitySystemComponent::ProcessAbilityInput(float DeltaTime, bool bGamePaused)
{
    // 。。。

    //
    // 按下触发的技能
    //
    for (const FGameplayAbilitySpecHandle& SpecHandle : InputPressedSpecHandles)
    {
        if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
        {
            if (AbilitySpec->Ability)
            {
                AbilitySpec->InputPressed = true;

                if (AbilitySpec->IsActive())
                {
                    // Ability is active so pass along the input event.
                    AbilitySpecInputPressed(*AbilitySpec);
                }
                else
                {
                    const UEqZeroGameplayAbility* EqZeroAbilityCDO = Cast<UEqZeroGameplayAbility>(AbilitySpec->Ability);

                    if (EqZeroAbilityCDO && EqZeroAbilityCDO->GetActivationPolicy() == EEqZeroAbilityActivationPolicy::OnInputTriggered)
                    {
                        AbilitiesToActivate.AddUnique(AbilitySpec->Handle);
                    }
                }
            }
        }
    }

    // 尝试激活所有通过按压和长按触发的能力。
    // 我们一次性完成所有操作,这样长按输入就不会激活该能力
    // 然后也不会因为按压而向该能力发送输入事件。
    for (const FGameplayAbilitySpecHandle& AbilitySpecHandle : AbilitiesToActivate)
    {
        TryActivateAbility(AbilitySpecHandle);
    }

    // 。。。
}

对于按下激活的技能。

如果已经激活了直接传递输入事件,会对 UAbilityTask_WaitInputPress 有影响

否则就加到容器里面等会激活

然后遍历容器,把刚刚按下触发和按住触发的技能触发。

void UEqZeroAbilitySystemComponent::ProcessAbilityInput(float DeltaTime, bool bGamePaused)
{
    // 。。。
    //
    // 处理所有松开触发的技能
    //
    for (const FGameplayAbilitySpecHandle& SpecHandle : InputReleasedSpecHandles)
    {
        if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
        {
            if (AbilitySpec->Ability)
            {
                AbilitySpec->InputPressed = false;

                if (AbilitySpec->IsActive())
                {
                    // Ability is active so pass along the input event.
                    AbilitySpecInputReleased(*AbilitySpec);
                }
            }
        }
    }

    InputPressedSpecHandles.Reset();
    InputReleasedSpecHandles.Reset();
}

松开的技能我们只是先触发事件。然后清空句柄。

发现按住的句柄没在这里清空,他说在松开的地方删除的,或者是Pawn更换要重新注册技能的时候。

ActivationGroupCounts

    int32 ActivationGroupCounts[(uint8)EEqZeroAbilityActivationGroup::MAX];

这个是拿来标记的数组,

/**
 * EEqZeroAbilityActivationGroup
 *
 *      定义技能激活时与其他技能的关系。
 *    - Independent: 独立技能,不受其他技能影响
 *    - Exclusive_Replaceable: 排他可替换技能,会被其他排他技能取消
 *    - Exclusive_Blocking: 排他阻塞技能,会阻止其他排他技能
 */
UENUM(BlueprintType)
enum class EEqZeroAbilityActivationGroup : uint8
{
    Independent,
    Exclusive_Replaceable,
    Exclusive_Blocking,
    MAX     UMETA(Hidden)
};

有这么几个接口

    /**
     * 技能激活组
     */
    UE_API bool IsActivationGroupBlocked(EEqZeroAbilityActivationGroup Group) const;
    UE_API void AddAbilityToActivationGroup(EEqZeroAbilityActivationGroup Group, UEqZeroGameplayAbility* EqZeroAbility);
    UE_API void RemoveAbilityFromActivationGroup(EEqZeroAbilityActivationGroup Group, UEqZeroGameplayAbility* EqZeroAbility);
    UE_API void CancelActivationGroupAbilities(EEqZeroAbilityActivationGroup Group, UEqZeroGameplayAbility* IgnoreEqZeroAbility, bool bReplicateCancelAbility);

EEqZeroAbilityActivationGroup ActivationGroup; 是我们自定义GA的一个属性

例如死亡技能激活的时候

void ULyraGameplayAbility_Death::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
    // ...
    // 尝试切换技能激活组 Exclusive_Blocking
    if (!ChangeActivationGroup(ELyraAbilityActivationGroup::Exclusive_Blocking))
    {
        UE_LOG(LogLyraAbilitySystem, Error, TEXT("ULyraGameplayAbility_Death::ActivateAbility: Ability [%s] failed to change activation group to blocking."), *GetName());
    }
}

如果能激活 LyraASC->AddAbilityToActivationGroup(NewGroup, this);

就会标记一下 ActivationGroupCounts[(uint8)Group]++;

void UEqZeroAbilitySystemComponent::AddAbilityToActivationGroup(EEqZeroAbilityActivationGroup Group, UEqZeroGameplayAbility* EqZeroAbility)
{
    check(EqZeroAbility);
    check(ActivationGroupCounts[(uint8)Group] < INT32_MAX);

    ActivationGroupCounts[(uint8)Group]++;

    const bool bReplicateCancelAbility = false;

    switch (Group)
    {
    case EEqZeroAbilityActivationGroup::Independent:
        // Independent abilities 什么也不需要
        break;

    case EEqZeroAbilityActivationGroup::Exclusive_Replaceable:
    case EEqZeroAbilityActivationGroup::Exclusive_Blocking:
        // 进来一个 可替代其他技能的技能,或者是阻塞其他技能的技能,就取消其他可被替代的技能Exclusive_Replaceable除了自己
        // 项目实际上所有技能都是独立的(没找到),只有死亡技能会强改一个 Exclusive_Blocking 写进来,然后尝试取消其他技能。
        // 都是其他技能开火啥的都是独立的,也没有需要取消的。
        CancelActivationGroupAbilities(EEqZeroAbilityActivationGroup::Exclusive_Replaceable, EqZeroAbility, bReplicateCancelAbility);
        break;

    default:
        checkf(false, TEXT("AddAbilityToActivationGroup: Invalid ActivationGroup [%d]\n"), (uint8)Group);
        break;
    }

    // 跑进去应该是逻辑错误了,才会有多个独占技能在运行
    const int32 ExclusiveCount = ActivationGroupCounts[(uint8)EEqZeroAbilityActivationGroup::Exclusive_Replaceable] + ActivationGroupCounts[(uint8)EEqZeroAbilityActivationGroup::Exclusive_Blocking];
    if (!ensure(ExclusiveCount <= 1))
    {
        UE_LOG(LogEqZeroAbilitySystem, Error, TEXT("AddAbilityToActivationGroup: Multiple exclusive abilities are running."));
    }
}

删除就比较简单了

void UEqZeroAbilitySystemComponent::RemoveAbilityFromActivationGroup(EEqZeroAbilityActivationGroup Group, UEqZeroGameplayAbility* EqZeroAbility)
{
    check(EqZeroAbility);
    check(ActivationGroupCounts[(uint8)Group] > 0);

    ActivationGroupCounts[(uint8)Group]--;
}

死亡技能加进来的时候是独立的。

触发的时候编程Blocking。死亡技能结束的时候,删除了Blocking。

在UEqZeroGameplayAbility::CanActivateAbility中

有一个判断会调用这个。

bool UEqZeroAbilitySystemComponent::IsActivationGroupBlocked(EEqZeroAbilityActivationGroup Group) const
{
    bool bBlocked = false;

    switch (Group)
    {
    case EEqZeroAbilityActivationGroup::Independent:
        // Independent abilities are never blocked.
        bBlocked = false;
        break;

    case EEqZeroAbilityActivationGroup::Exclusive_Replaceable:
    case EEqZeroAbilityActivationGroup::Exclusive_Blocking:
        // Exclusive abilities can activate if nothing is blocking.
        bBlocked = (ActivationGroupCounts[(uint8)EEqZeroAbilityActivationGroup::Exclusive_Blocking] > 0);
        break;

    default:
        checkf(false, TEXT("IsActivationGroupBlocked: Invalid ActivationGroup [%d]\n"), (uint8)Group);
        break;
    }

    return bBlocked;
}

配置了 Exclusive_Replaceable 和 Exclusive_Blocking 的技能会被 例如死亡状态不让激活

InitAbilityActorInfo


void UEqZeroAbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor)
{
    // 挂在 PlayerState 上,但是由于时序,可能pawn ext 和 player state 都有可能调用过来

    FGameplayAbilityActorInfo* ActorInfo = AbilityActorInfo.Get();
    check(ActorInfo);
    check(InOwnerActor);

    const bool bHasNewPawnAvatar = Cast<APawn>(InAvatarActor) && (InAvatarActor != ActorInfo->AvatarActor);

    Super::InitAbilityActorInfo(InOwnerActor, InAvatarActor);

    // 如果 avatar 变了,重新初始化
    if (bHasNewPawnAvatar)
    {
        // Notify all abilities that a new pawn avatar has been set
        for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
        {
PRAGMA_DISABLE_DEPRECATION_WARNINGS
            ensureMsgf(AbilitySpec.Ability && AbilitySpec.Ability->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced, TEXT("InitAbilityActorInfo: All Abilities should be Instanced (NonInstanced is being deprecated due to usability issues)."));
PRAGMA_ENABLE_DEPRECATION_WARNINGS

            TArray<UGameplayAbility*> Instances = AbilitySpec.GetAbilityInstances();
            for (UGameplayAbility* AbilityInstance : Instances)
            {
                UEqZeroGameplayAbility* EqZeroAbilityInstance = Cast<UEqZeroGameplayAbility>(AbilityInstance);
                if (EqZeroAbilityInstance)
                {
                    // Ability instances may be missing for replays
                    EqZeroAbilityInstance->OnPawnAvatarSet();
                }
            }
        }

        // 全局注册一下ASC,这样就能一下子让全部角色播放技能了。这是一个WorldSubSystem
        if (UEqZeroGlobalAbilitySystem* GlobalAbilitySystem = UWorld::GetSubsystem<UEqZeroGlobalAbilitySystem>(GetWorld()))
        {
            GlobalAbilitySystem->RegisterASC(this);
        }

        // 重新关联动画和ASC
        if (UEqZeroAnimInstance* EqZeroAnimInst = Cast<UEqZeroAnimInstance>(ActorInfo->GetAnimInstance()))
        {
            EqZeroAnimInst->InitializeWithAbilitySystem(this);
        }

        // 激活OnSpawn的时候就要激活的技能
        TryActivateAbilitiesOnSpawn();
    }
}

ASC初始化的时候,额外处理了Pawn可能会变化的情况?

什么情况会变?

然后激活Policy是OnSpawn的技能,这是我们自己定义的激活方式

技能的激活和取消

void UEqZeroAbilitySystemComponent::TryActivateAbilitiesOnSpawn()
{
    // 激活OnSpawn的时候就要激活的技能
    ABILITYLIST_SCOPE_LOCK();
    for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
    {
        if (const UEqZeroGameplayAbility* EqZeroAbilityCDO = Cast<UEqZeroGameplayAbility>(AbilitySpec.Ability))
        {
            EqZeroAbilityCDO->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), AbilitySpec);
        }
    }
}
void UEqZeroAbilitySystemComponent::CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility)
{
    // 简单概况就是 通过 ShouldCancelFunc 检查需要取激活的技能,取消调用

}

从GM指令发起的取消技能

void UEqZeroAbilitySystemComponent::CancelInputActivatedAbilities(bool bReplicateCancelAbility)
{
    auto ShouldCancelFunc = [this](const UEqZeroGameplayAbility* EqZeroAbility, FGameplayAbilitySpecHandle Handle)
    {
        const EEqZeroAbilityActivationPolicy ActivationPolicy = EqZeroAbility->GetActivationPolicy();
        return ((ActivationPolicy == EEqZeroAbilityActivationPolicy::OnInputTriggered) || (ActivationPolicy == EEqZeroAbilityActivationPolicy::WhileInputActive));
    };

    CancelAbilitiesByFunc(ShouldCancelFunc, bReplicateCancelAbility);
}

技能输入事件

技能事件,具体技能再扩展


void UEqZeroAbilitySystemComponent::AbilitySpecInputPressed(FGameplayAbilitySpec& Spec)
{
    Super::AbilitySpecInputPressed(Spec);

    // ...
}

void UEqZeroAbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& Spec)
{
    Super::AbilitySpecInputReleased(Spec);

    // ...
}

技能回调

NotifyAbilityActivated

NotifyAbilityFailed

NotifyAbilityEnded

技能触发,就有激活成功,失败,结束的生命周期在ASC上的回调

ApplyAbilityBlockAndCancelTags

GetAdditionalActivationTagRequirements

void UEqZeroAbilitySystemComponent::ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags)
{
    FGameplayTagContainer ModifiedBlockTags = BlockTags;
    FGameplayTagContainer ModifiedCancelTags = CancelTags;

    if (TagRelationshipMapping)
    {
        // Use the mapping to expand the ability tags into block and cancel tag
        TagRelationshipMapping->GetAbilityTagsToBlockAndCancel(AbilityTags, &ModifiedBlockTags, &ModifiedCancelTags);
    }

    Super::ApplyAbilityBlockAndCancelTags(AbilityTags, RequestingAbility, bEnableBlockTags, ModifiedBlockTags, bExecuteCancelTags, ModifiedCancelTags);

    //@TODO: Apply any special logic like blocking input or movement
}

void UEqZeroAbilitySystemComponent::GetAdditionalActivationTagRequirements(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer& OutActivationRequired, FGameplayTagContainer& OutActivationBlocked) const
{
    if (TagRelationshipMapping)
    {
        TagRelationshipMapping->GetRequiredAndBlockedActivationTags(AbilityTags, &OutActivationRequired, &OutActivationBlocked);
    }
}

相当于技能又自己的阻塞,取消流程,都是为了配置同一到一个地方抽了出来,在这里应用

HandleChangeAbilityCanBeCanceled

没用到

AddDynamicTagGameplayEffect

RemoveDynamicTagGameplayEffect

包了一层能直接对ASC加GE的简易接口。

GetAbilityTargetData 和预测有关,后面解析

这样 UEqZeroAbilitySystemComponent 基本看完了。还有一些比较短的文件也塞这里吧

UEqZeroAbilitySystemGlobals

UCLASS(Config=Game)
class UEqZeroAbilitySystemGlobals : public UAbilitySystemGlobals
{
    GENERATED_UCLASS_BODY()

    //~UAbilitySystemGlobals interface
    virtual FGameplayEffectContext* AllocGameplayEffectContext() const override;
    //~End of UAbilitySystemGlobals interface
};

主要是为了关联一下GE类是我们自己的,这个具体要和技能一起看

FGameplayEffectContext* UEqZeroAbilitySystemGlobals::AllocGameplayEffectContext() const
{
    return new FEqZeroGameplayEffectContext();
}

UEqZeroAbilityTagRelationshipMapping

ASC的时候解析过,描述技能的阻塞关系,因为原版配置太分散了

UEqZeroGlobalAbilitySystem

ASC的注册和反注册

为了全局能拿到所有ASC一起播技能,例如暖场无敌等。

AEqZeroTaggedActor

主角的mesh是不可见的。

这个Actor配置了一个mesh,然后挂到主角上面,提供可见的mesh。

这样就可以主角mesh控制动画蓝图,子节点mesh是不同的模型,例如男,女角色通过 拷贝姿势的方式运作

具体要在装扮模块里面展开。前面动画模块也提到了。

UEqZeroAttributeSet

项目对 UAttributeSet 包装的基类,主要封装了 GetWorld 和 GetEqZeroAbilitySystemComponent 接口

UCLASS(MinimalAPI)
class UEqZeroAttributeSet : public UAttributeSet
{
    GENERATED_BODY()

public:

    UE_API UEqZeroAttributeSet();

    UE_API UWorld* GetWorld() const override;

    UE_API UEqZeroAbilitySystemComponent* GetEqZeroAbilitySystemComponent() const;
};

他的子类

UEqZeroHealthSet 包括 生命,最大生命,治疗,伤害

UEqZeroCombatSet 包括 基础伤害,基础治疗

上一篇
下一篇