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 包括 基础伤害,基础治疗