Nov 10, 2025
36 mins read
该图形状方框代表函数,菱形代表蓝图、连接线不代表任何继承关系,仅用作思维导图。

DA_InputConfig中配置了IMC、移动和视角的NativeInputActions和AbilityInputActions(tag和inputaction的映射结构体)
角色类中SetupPlayerInputComponent通过WarriorInputComponent::BindAbilityInputAction绑定玩家的移动和视角旋转的输入,绑定能力的输入,还有设置回调函数Input_AbilityInputPressed(去调用自定义ASC中的OnAbilityInputPressed,其中会TryActivateAbility),对该结构体数组中每个元素绑定。
角色类中PossessedBy中同步加载DA_StartUpData,调用DataAsset_StartUpDataBase的GiveToAbilitySystemComponent去赋予初始时就有的能力,通过调用GrantAbilityies去激活ActivateOnGivenAbilities(UI显示、生成武器)和ReactiveAbilities(受击能力)。在这个函数中只是GiveAbility装备技能,但是激活逻辑不在这里,而在GA基类中(OnGiveAbility就会激活能力)。
因为有一个AbilityActivationPolicy枚举,在GA基类中定义,在OnGiveAbility(虚函数)会判断这个GA在装配时需不需要激活,如果需要呢么就会TryActivateAbility。其中典型代表GA生成武器。
当然DataAsset_HeroStartUpData中的GiveToAbilitySystemComponent(继承DataAsset_StartUpDataBase)会额外装配角色初始化的能力(GA装配武器),但是该GA的AbilityActivationPolicy不是OnGiven,所以不会在这里激活,而是在输入绑定后按键激活。
装配武器之后会链接动画类层(目的是去调用武器对应的动画层),添加武器对应的输入映射,还有赋予武器的能力(卸下武器、轻击、重击)
动画的逻辑图:

蓝图函数库是静态函数的集合,提供与Gameplay对象无关的实用工具功能,
比如UGameplayStatics和UKismetSystemLibrary
有时候你写游戏逻辑,会发现:
“我想写一个通用的小函数,比如把某个 Actor 转成 Pawn 并拿到它的 AIController。”
但这个逻辑既不是某个角色特有的、也不是某个组件专属的。它只是一个到处都能用的“小工具函数”。这时候你就不该把它写在 Actor、Component、GameInstance 里,因为那样会让这个逻辑绑死在具体类型上。
👉 所以 Unreal 提供了一个专门的“函数容器”——UBlueprintFunctionLibrary。
PawnExtensionComponentBase
|__PawnCombatComponent
|__HeroCombatComponent
|__EnemyCombatComponent
static_assert 是 编译期断言,会在编译时检查条件是否成立,不成立就报编译错误。
创建一个PawnExtensionComponentBase,用于角色组件,统一访问Pawn、Controller等。
protected:
template<class T>
T* GetOwningPawn() const
{
// TPointerIsConvertibleFromTo 测试 T 是否可转换为 APawn
static_assert(TPointerIsConvertibleFromTo<T,APawn>::Value, "'T' Template Parameter to GetPawn must be derived from APawn");
return CastChecked<T>(GetOwner());
}
APawn* GetOwningPawn() const
{
return GetOwningPawn<APawn>();
}
template<class T>
T* GetOwningController() const
{
static_assert(TPointerIsConvertibleFromTo<T,AController>::Value, "'T' Template Parameter to GetController must be derived from AController");
return GetOwningPawn<APawn>()->GetController<T>();
}
之后在WarriorHeroCharacter中定义一个能够获取HeroCombatComponent组件的函数。
FORCEINLINE UHeroCombatComponent* GetHeroCombatComponent() const { return HeroCombatComponent; }
PawnCombatComponent中定义一个map存储tag-weapon。
三个函数分别是注册生成的weapon到map中;通过tag获取角色携带的武器;获取角色当前装配的武器。
将会在蓝图中调用这些函数。当GA_Shared_SpawnWeapon中生成武器后调用RegisterSpawnedWeapon
public:
UFUNCTION(BlueprintCallable, Category = "Warrior|Combat")
void RegisterSpawnedWeapon(FGameplayTag InWeaponTagToRegister, AWarriorWeaponBase* InWeaponToRegister, bool bRegisterAsEquippedWeapon = false);
UFUNCTION(BlueprintCallable, Category = "Warrior|Combat")
AWarriorWeaponBase* GetCharacterCarriedWeaponByTag(FGameplayTag InWeaponTagToGet) const;
UPROPERTY(BlueprintReadWrite, Category = "Warrior|Combat")
FGameplayTag CurrentEquippedWeaponTag;
UFUNCTION(BlueprintCallable, Category = "Warrior|Combat")
AWarriorWeaponBase* GetCharacterCurrentEquippedWeapon() const;
private:
TMap<FGameplayTag, AWarriorWeaponBase*> CharacterCarriedWeaponMap;
// 把生成好的武器注册到角色的已持有武器表中,并可选地设为当前装备武器。
void UPawnCombatComponent::RegisterSpawnedWeapon(FGameplayTag InWeaponTagToRegister,
AWarriorWeaponBase* InWeaponToRegister, bool bRegisterAsEquippedWeapon)
{
checkf(!CharacterCarriedWeaponMap.Contains(InWeaponTagToRegister), TEXT("A named %s has already been added as carried weapon"),*InWeaponTagToRegister.ToString());
CharacterCarriedWeaponMap.Emplace(InWeaponTagToRegister, InWeaponToRegister);
if (bRegisterAsEquippedWeapon)
{
CurrentEquippedWeaponTag = InWeaponTagToRegister;
}
}
// 根据武器标签,返回对应的武器对象指针
AWarriorWeaponBase* UPawnCombatComponent::GetCharacterCarriedWeaponByTag(FGameplayTag InWeaponTagToGet) const
{
if (CharacterCarriedWeaponMap.Contains(InWeaponTagToGet))
{
if (AWarriorWeaponBase* const* FoundWeapon = CharacterCarriedWeaponMap.Find(InWeaponTagToGet))
{
return *FoundWeapon;
}
}
return nullptr;
}
// 获取当前装备的武器
AWarriorWeaponBase* UPawnCombatComponent::GetCharacterCurrentEquippedWeapon() const
{
if (!CurrentEquippedWeaponTag.IsValid())
{
return nullptr;
}
return GetCharacterCarriedWeaponByTag(CurrentEquippedWeaponTag);
}
之后就可以在Hero_SpawnAxe中配置注册的tag了。

在WarriorGameAbility中添加两个新函数用于获取CombatComponent和ASC。
UFUNCTION(BlueprintPure, Category = "Warrior|Ability")
UPawnCombatComponent* GetPawnCombatComponentFromActorInfo() const;
UFUNCTION(BlueprintPure, Category = "Warrior|Ability")
UWarriorAbilitySystemComponent* GetWarriorAbilitySystemComponentFromActorInfo() const;
UPawnCombatComponent* UWarriorGameplayAbility::GetPawnCombatComponentFromActorInfo() const
{
return GetAvatarActorFromActorInfo()->FindComponentByClass<UPawnCombatComponent>();
}
UWarriorAbilitySystemComponent* UWarriorGameplayAbility::GetWarriorAbilitySystemComponentFromActorInfo() const
{
return Cast<UWarriorAbilitySystemComponent>(CurrentActorInfo->AbilitySystemComponent);
}
然后创建WarriorHeroGameplayAbility去继承WarriorGameAbility
public:
UFUNCTION(BlueprintPure, Category = "Warrior|Ability")
AWarriorHeroCharacter* GetHeroCharacterFromActorInfo();
UFUNCTION(BlueprintPure, Category = "Warrior|Ability")
AWarriorHeroController* GetHeroControllerFromActorInfo();
UFUNCTION(BlueprintPure, Category = "Warrior|Ability")
UHeroCombatComponent* GetHeroCombatComponentFromActorInfo();
private:
TWeakObjectPtr<AWarriorHeroCharacter> CachedWarriorHeroCharacter;
TWeakObjectPtr<AWarriorHeroController> CachedWarriorHeroController;
AWarriorHeroCharacter* UWarriorHeroGameplayAbility::GetHeroCharacterFromActorInfo()
{
if (!CachedWarriorHeroCharacter.IsValid())
{
CachedWarriorHeroCharacter = Cast<AWarriorHeroCharacter>(CurrentActorInfo->AvatarActor);
}
return CachedWarriorHeroCharacter.IsValid() ? CachedWarriorHeroCharacter.Get() : nullptr;
}
AWarriorHeroController* UWarriorHeroGameplayAbility::GetHeroControllerFromActorInfo()
{
if (!CachedWarriorHeroController.IsValid())
{
CachedWarriorHeroController = Cast<AWarriorHeroController>(CurrentActorInfo->PlayerController);
}
return CachedWarriorHeroController.IsValid() ? CachedWarriorHeroController.Get() : nullptr;
}
UHeroCombatComponent* UWarriorHeroGameplayAbility::GetHeroCombatComponentFromActorInfo()
{
return GetHeroCharacterFromActorInfo()->GetHeroCombatComponent();
}
创建一个GA蓝图WarriorHeroGameplayAbility,在当中可以调用这些函数去获取组件、controller、character。
绑定装配武器的输入
在WarriorGameplayTags中添加新Tag
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_EquipAxe);
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_UnEquipAxe);
DataAsset_InputConfig中定义
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (TitleProperty = "InputTag"))
TArray<FWarriorInputActionConfig> AbilityInputActions;
在WarriorInputComponent中定义BindAbilityInputAction来绑定能力输入。
为每个技能Action(FWarriorInputActionConfig)分别绑定:
ETriggerEvent::Started → 调用 InputPressedFuncETriggerEvent::Completed → 调用 InputReleasedFunctemplate <class UserObject, typename CallbackFunc>
inline void UWarriorInputComponent::BindAbilityInputAction(const UDataAsset_InputConfig* InInputConfig, UserObject* ContextObject,
CallbackFunc InputPressedFunc, CallbackFunc InputReleasedFunc)
{
checkf(InInputConfig,TEXT("InputConfigDataAsset is null"));
for (const FWarriorInputActionConfig& AbilityInputActionConfig : InInputConfig->AbilityInputActions)
{
if (!AbilityInputActionConfig.IsValid()) continue;
BindAction(AbilityInputActionConfig.InputAction, ETriggerEvent::Started, ContextObject, InputPressedFunc, AbilityInputActionConfig.InputTag);
BindAction(AbilityInputActionConfig.InputAction, ETriggerEvent::Completed, ContextObject, InputReleasedFunc, AbilityInputActionConfig.InputTag);
}
}
在DataAsset_HeroStartUpData中定义结构体FWarriorHeroAbilitySet。该结构体包含角色初始化所需的技能。
USTRUCT(BlueprintType)
struct FWarriorHeroAbilitySet
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (Categories = "InputTag"))
FGameplayTag InputTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<UWarriorGameplayAbility> AbilityToGrant;
bool IsValid() const;
};
在该文件中重写父类DataAsset_StartUpDataBase的GiveToAbilitySystemComponent,这个函数在角色类中的PossessedBy中被调用。
void UDataAsset_HeroStartUpData::GiveToAbilitySystemComponent(UWarriorAbilitySystemComponent* InASCToGive,
int32 ApplyLevel)
{
Super::GiveToAbilitySystemComponent(InASCToGive, ApplyLevel);
for (const FWarriorHeroAbilitySet& AbilitySet : HeroStartUpAbilitySets)
{
if (!AbilitySet.IsValid()) continue;
FGameplayAbilitySpec AbilitySpec(AbilitySet.AbilityToGrant);
AbilitySpec.SourceObject = InASCToGive->GetAvatarActor();
AbilitySpec.Level = ApplyLevel;
AbilitySpec.DynamicAbilityTags.AddTag(AbilitySet.InputTag);
InASCToGive->GiveAbility(AbilitySpec);
}
}
在WarriorAbilitySystemComponent中定义两个函数,用于绑定输入的回调函数。
void OnAbilityInputPressed(const FGameplayTag& InInputTag);
void OnAbilityInputReleased(const FGameplayTag& InInputTag);
void UWarriorAbilitySystemComponent::OnAbilityInputPressed(const FGameplayTag& InInputTag)
{
if (!InInputTag.IsValid())
{
return;
}
for (const FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
{
if (!AbilitySpec.DynamicAbilityTags.HasTagExact(InInputTag)) continue;
TryActivateAbility(AbilitySpec.Handle);
}
}
同样的在WarriorHeroCharacter里也有调用刚才WarriorAbilitySystemComponent定义的函数
void AWarriorHeroCharacter::Input_AbilityInputPressed(FGameplayTag InInputTag)
{
WarriorAbilitySystemComponent->OnAbilityInputPressed(InInputTag);
}
void AWarriorHeroCharacter::Input_AbilityInputReleased(FGameplayTag InInputTag)
{
WarriorAbilitySystemComponent->OnAbilityInputReleased(InInputTag);
}
这两个函数目的是为了调用WarriorInputComponent中定义的BindAbilityInputAction
WarriorInputComponent->BindAbilityInputAction(InputConfigDataAsset, this, &ThisClass::Input_AbilityInputPressed, &ThisClass::Input_AbilityInputReleased);

赋予能力:DataAsset_HeroStartUpData中重写父类的GiveToAbilitySystemComponent,在其中调用WarriorAbilitySystemComponent的GiveAbility去赋予能力。
激活能力:
在角色类的SetupPlayerInputComponent中调用WarriorInputComponent的BindAbilityInputAction,并传入按下和释放按键的回调函数。
DA_InputConfig中有配置好Input Tag和InputAction,也就是FWarriorInputActionConfig结构体。

BindAbilityInputAction负责把输入系统和GAS连接起来,通过传入InputTag使当按下输入时,调用带 InputTag 的函数,也就是角色类的回调函数Input_AbilityInputPressed、Input_AbilityInputReleased。
这两个回调函数会调用WarriorAbilitySystemComponent的OnAbilityInputPressed、OnAbilityInputReleased函数。
其中ASC系统中OnAbilityInputPressed会通过TryActivateAbility来实现GA。
首先创建动画通知蓝图类,添加一个重写函数ReceivedNotify。

再装配武器的动画蒙太奇中添加该动画通知,并在Details面板中设置EventTag为新建的Player.Event.Equip.Axe。
在GA_Hero_EquipAxe中播放蒙太奇并在接收到GameplayEvent时使武器附着在右手上。


创建动画层接口ALI_Hero,之后再ABP_Hero中的类设置配置继承接口层为这个动画接口。
由于ABP_Hero继承C++中的WarriorCharacterAnimInstance,在该文件中有AWarriorHeroCharacter* OwningHeroCharacter;因此可以通过属性存取获得到OwningCharacter的Combat组件,也就可以得到当前武器的Tag。因此可以来混合拿武器和不拿武器的动画。

之后创建MasterLayer_Hero继承WarriorHeroLinkedAnimLayer,同样在类设置配置继承接口层为ALI_Hero。
在WarriorHeroLinkedAnimLayer中创建该函数,用于获取AnimInstance。
UWarriorHeroAnimInstance* UWarriorHeroLinkedAnimLayer::GetHeroAnimInstance() const
{
return Cast<UWarriorHeroAnimInstance>(GetOwningComponent()->GetAnimInstance()) ;
}

之后在创建一个继承MasterLayer_Hero的AnimLayer_HeroAxe,用于播放持有武器的混合动画。在类默认中配置DefaultLocomotionBlendSpace为新建的BlendSpace1D持有武器的混合空间动画。
接下来需要将这些层贯穿起来。
创建新的cpp文件类型为None用于自定义结构体。首先定义一个FWarriorHeroWeaponData
class UWarriorHeroLinkedAnimLayer;
USTRUCT(BlueprintType)
struct FWarriorHeroWeaponData
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TSubclassOf<UWarriorHeroLinkedAnimLayer> WeaponAnimLayerToLink;
};
之后要在武器类中获取这个结构体,这样暴露在蓝图中可以配置结构体的WarriorHeroLinkedAnimLayer。
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "WeaponData")
FWarriorHeroWeaponData HeroWeaponData;

然后在HeroCombatComponent中定义一个通过Tag获取角色持有武器的函数。与父类函数相同,只不过要做一个强转而已。
AWarriorHeroWeapon* UHeroCombatComponent::GetHeroCarriedWeaponByTag(FGameplayTag InWeaponTag) const
{
return Cast<AWarriorHeroWeapon>(GetCharacterCarriedWeaponByTag(InWeaponTag));
}
在GA中添加如下的逻辑,通过LinkAnimClassLayers来实现关联动画层。这样就可以完成了武器装配后动画的切换了。

声明GameplayTag
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Equip_Axe);
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_UnEquip_Axe);
将tag设置到GA中

之后创建收回武器的蒙太奇动画,添加动画通知并设置EventTag。在GA蓝图中像装配武器那样处理接收到event之后的逻辑。
创建一个InputAction用于收回武器的输入触发,并配置在DA_InputConfig中,同样需要一个GameplayTag。
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_UnEquipAxe);

思考一个问题:玩家卸下武器的能力应该要在玩家出生时就拥有,还是在装配武器后才拥有。
显而易见,应该在装配武器后才会拥有卸下武器的能力,因此我们需要在HeroWeaponData中(那个结构体,已有AnimLayerToLink成员)添加一些新的成员。
HeroWeaponData
|__AnimLayerToLink(切换持有武器的动画)
|__DefaultWeaponAbility(获取武器能力,比如卸下武器、轻击、重击)
|__InputMappingContext(更改按键绑定)
将FWarriorHeroAbilitySet结构体从DataAsset_HeroStartUpData移动到WarriorStructType中。之后定义新的成员分别是WeaponInputMappingContext和DefaultWeaponAbilities
USTRUCT(BlueprintType)
struct FWarriorHeroWeaponData
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TSubclassOf<UWarriorHeroLinkedAnimLayer> WeaponAnimLayerToLink;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
UInputMappingContext* WeaponInputMappingContext;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (TitleProperty = "InputTag"))
TArray<FWarriorHeroAbilitySet> DefaultWeaponAbilities;
};
创建一个新的IMC,其中有武器能力的输入,在BP_HeroAxe中修改HeroWeaponData。

在WarriorAbilitySystemComponent中增加一个函数用于赋予武器能力。
在GiveAbility后GAS会返回一个FGameplayAbilitySpecHandle,OutGrantedAbilitySpecHandles 用来收集并保存每个被授予的能力的句柄,方便你之后按句柄精确地移除、查找或管理这些能力。
void UWarriorAbilitySystemComponent::GrantHeroWeaponAbilities(
const TArray<FWarriorHeroAbilitySet>& InDefaultWeaponAbilities, int32 ApplyLevel,
TArray<FGameplayAbilitySpecHandle>& OutGrantedAbilitySpecHandles)
{
if (InDefaultWeaponAbilities.IsEmpty())
{
return;
}
for (const FWarriorHeroAbilitySet& AbilitySet : InDefaultWeaponAbilities)
{
if (!AbilitySet.IsValid()) continue;
FGameplayAbilitySpec AbilitySpec(AbilitySet.AbilityToGrant);
AbilitySpec.SourceObject = GetAvatarActor();
AbilitySpec.Level = ApplyLevel;
AbilitySpec.DynamicAbilityTags.AddTag(AbilitySet.InputTag);
OutGrantedAbilitySpecHandles.AddUnique(GiveAbility(AbilitySpec));
}
}
在GA_Hero_EquipAxe中创建新函数HandleEquipWeapon用来替代之前写的逻辑。

在WarriorHeroWeapon中添加两个函数。装配武器蓝图中在GrantHeroWeaponAbilities后面调用AssignGrantedAbilitySpecHandles。
void AWarriorHeroWeapon::AssignGrantedAbilitySpecHandles(const TArray<FGameplayAbilitySpecHandle>& InSpecHandles)
{
GrantAbilitySpecHandles = InSpecHandles;
}
TArray<FGameplayAbilitySpecHandle> AWarriorHeroWeapon::GetGrantedAbilitySpecHandles() const
{
return GrantAbilitySpecHandles;
}
在WarriorAbilitySystemComponent中添加一个新函数,用于移除武器赋予的能力。
void UWarriorAbilitySystemComponent::RemoveGrantedHeroWeaponAbilities(
TArray<FGameplayAbilitySpecHandle>& InSpecHandlesToRemove)
{
if (InSpecHandlesToRemove.IsEmpty())
{
return;
}
for (const FGameplayAbilitySpecHandle& SpecHandle : InSpecHandlesToRemove)
{
if (SpecHandle.IsValid())
{
ClearAbility(SpecHandle);
}
}
InSpecHandlesToRemove.Empty();
}
卸下武器的GA中写如下逻辑

同样需要声明GameplayTag。
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_LightAttack_Axe);
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_HeavyAttack_Axe);
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Light_Axe);
WARRIOR_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Heavy_Axe);
创建新的GA:GA_Hero_LightAttackMaster和GA_Hero_LightAttack_Axe(继承前一个master)。配置好标签,如下图所示,并且将Equip和UnEquip的Block也添加Attack的标签。

创建新的IA_LightAttack_Axe,并添加到DA_InputConfig和IMC_Axe中。
在BP_HeroAxe的WeaponData中添加新的元素。
能力实例化策略
在轻击这里使用第二种策略,也就是只实例化一次,之后复用。
在GA_Hero_LightAttackMaster创建一个Map用来存储连击次数与动画的映射。
在每次EndAbility后设置一个定时器,如果在定时器的时间内没有进行下一次攻击,则会调用自定义事件——ResetAttackComboCount(将连击次数重置为1)。如果在规定时间内进行了连击,则会进入到激活能力的流程中清除定时器(也就不会执行自定义Event)。
根据连击次数调用蒙太奇动画,如果连击次数达到Map的容量,则清空连击次数(调用自定义Event),否则连击数++。

之后创建连击动画的蒙太奇动画,并更改插槽为FullBody,在ABP_Hero中添加该插槽。之后再配置GA中的Map。就可以实现连击了。

重击与轻击相同逻辑,不再赘述。重击会有2次连击效果。
在本节中会创建一个蓝图函数库的C++类。
由于蓝图只能使用执行引脚,不能用bool返回节点控制流,因此使用ExpandEnumAsExecs让枚举展开成多个执行引脚。DisplayName指定蓝图节点上显示的名字(而不是函数名)。
UENUM()
enum class EWarriorConfirmType :uint8
{
Yes,
No
};
UCLASS()
class WARRIOR_API UWarriorFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
static UWarriorAbilitySystemComponent* NativeGetWarriorASCFromActor(AActor* InActor);
UFUNCTION(BlueprintCallable, Category = "Warrior|FunctionLibrary")
static void AddGameplayTagToActorIfNone(AActor* InActor, FGameplayTag TagToAdd);
UFUNCTION(BlueprintCallable, Category = "Warrior|FunctionLibrary")
static void RemoveGameplayFromActorIfFound(AActor* InActor, FGameplayTag TagToRemove);
// C++调用
static bool NativeDoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck);
// 蓝图调用
UFUNCTION(BlueprintCallable, Category = "Warrior|FunctionLibrary", meta = (DisplayName = "Does Actor Have Tag", ExpandEnumAsExecs = "OutConfirmType"))
static void BP_DoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck, EWarriorConfirmType& OutConfirmType);
};
UWarriorAbilitySystemComponent* UWarriorFunctionLibrary::NativeGetWarriorASCFromActor(AActor* InActor)
{
check(InActor);
return CastChecked<UWarriorAbilitySystemComponent>(UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(InActor));
}
void UWarriorFunctionLibrary::AddGameplayTagToActorIfNone(AActor* InActor, FGameplayTag TagToAdd)
{
UWarriorAbilitySystemComponent* ASC = NativeGetWarriorASCFromActor(InActor);
if (!ASC->HasMatchingGameplayTag(TagToAdd))
{
ASC->AddLooseGameplayTag(TagToAdd); // 不会触发激活逻辑 添加临时状态
}
}
void UWarriorFunctionLibrary::RemoveGameplayFromActorIfFound(AActor* InActor, FGameplayTag TagToRemove)
{
UWarriorAbilitySystemComponent* ASC = NativeGetWarriorASCFromActor(InActor);
if (ASC->HasMatchingGameplayTag(TagToRemove))
{
ASC->RemoveLooseGameplayTag(TagToRemove);
}
}
bool UWarriorFunctionLibrary::NativeDoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck)
{
UWarriorAbilitySystemComponent* ASC = NativeGetWarriorASCFromActor(InActor);
return ASC->HasMatchingGameplayTag(TagToCheck);
}
void UWarriorFunctionLibrary::BP_DoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck,
EWarriorConfirmType& OutConfirmType)
{
OutConfirmType = NativeDoesActorHaveTag(InActor, TagToCheck) ? EWarriorConfirmType::Yes : EWarriorConfirmType::No;
}
JumpToFinsher是一个新的临时Tag,目的是当轻击次数达到最后一次之前,如果使用重击,则直接跳跃到重击的最后一个连击效果。
例子:轻击有4个,重击有2个。当轻击到第三下时使用重击会直接跳到重击的第二个效果。
UE_DEFINE_GAMEPLAY_TAG(Player_Status_JumpToFinisher, "Player.Status.JumpToFinisher");

在EndAbility后也需要调用RemoveGameplayFromActorIfFound。

Sharing is caring!