![見出し画像](https://assets.st-note.com/production/uploads/images/122541173/rectangle_large_type_2_84c0af7113e53fe4c94a1d4b2e683b73.png?width=1200)
【Unreal Engine5】InputTagとGameplayAbility【後編】
※注意※
・この記事はUnreal Engine 5でC++を使った実装をやっている記事です。Blueprintも使いますが、主にC++について記述しているので、Blueprintだけで実装する類の記事ではありません。
・C++を使って実装する部分やソースコードが多々出てくるので、ある程度C++が読める方向けです(雰囲気はわかるように書くつもりですが、仕組みがある程度分かっている方が見た方がいいかもしれません)
・これが必ずしも正しい!一番効率的!というものではないので、間違っている部分や考え方がおかしい部分もあることをご承知おきください(本業が企画職なので知識が浅いです)
上記内容でも気になるという方は続きをお読みください。
前準備→https://note.com/mok0/n/nd5914eac5045
これが終わっている状態からのスタートになります。このプロジェクトを改造していく形になります。
引き続き「Lyra」というUnreal Engine5のサンプルゲームの中から入力周りについての拡張部分をUE5のサードパーソンプロジェクト(C++版)に移植するものになります。
前回はGameplayAbilitySystemの前準備として、前々回で作ったプログラム達を改良し、GameplayAbilityに対応できるように機能追加しました。
今回は後編の「AbilitySysytemComponet」と「GamplayAbility」の2つを拡張してCharacterクラスに実装をやっていければと思います。
ここから本題
※〇〇となっているところは任意で大丈夫ですが、今回はTagInputSampleの略で「TIS」とすべて入力しています。
◆今回やったこと
・AbilitySetとInputCofigの作成と修正
・GamplayAbilityを継承したクラスを作成し拡張する
・AbilitySysytemComponetを継承したクラスを作成し拡張する
・CharacterにGameplayAbilitySysytemの実装をする
・JumpのAbilityを実装する
◆AbilitySetとInputCofigの作成と修正
今回はアビリティとしてJumpを実装してみようと思います。よくある例ではありますがJumpの実行についてをGameplayAbilityを使って行います。
前回InputConfigを拡張したと思いますので、以下の画像のように設定をしてみましょう。
![](https://assets.st-note.com/img/1700788698988-UoPgtNGnzv.png?width=1200)
次に新規で作成したDataAssetの「AbilitySet」を作成します。
コンテンツブラウザでデータアセットを作成し、継承するクラスを「〇〇AbilitySet」としましょう。
出来上がったデータアセットを開き、画像の様に設定してみます。
![](https://assets.st-note.com/img/1700788749125-lWhYIZiQPz.png?width=1200)
するとAbilityの部分が作れないと思います。
ここがGameplayAbilityのアビリティファイルになるのですが、今回はそのアビリティファイルを作るクラスも拡張をすることになります。
※通常で使う分には拡張は必要せず、用意されたもので実行などがすべてできる状態になります。
ここから先は動作まで持っていけましたが、全部を読み解くまでに至らなかったため、あやふやな説明がある部分となります(すいません)
ただ、「Lyra」の中からこの部分の移植までで動いていることは確認できています。
※いつかこの辺りの細かい部分を説明できるくらいに理解はしたい内容なので地道に続けていこうとは思っています。
◆GamplayAbilityを継承したクラスを作成し拡張する
前回作成した「〇〇GameplayAbility」の追記をしていきます。
[コード]〇〇GameplayAbility.h
#pragma once
#include "CoreMinimal.h"
#include "Abilities/GameplayAbility.h"
#include "〇〇GameplayAbility.generated.h"
struct FGameplayAbilityActivationInfo;
struct FGameplayAbilitySpec;
struct FGameplayAbilitySpecHandle;
class AActor;
class AController;
class APlayerController;
class FText;
class UAnimMontage;
class UObject;
struct FFrame;
struct FGameplayAbilityActorInfo;
struct FGameplayEffectSpec;
struct FGameplayEventData;
class U〇〇AbilitySystemComponent;
UENUM(BlueprintType)
enum class E〇〇AbilityActivationPolicy : uint8
{
// Try to activate the ability when the input is triggered.
OnInputTriggered,
// Continually try to activate the ability while the input is active.
WhileInputActive,
// Try to activate the ability when an avatar is assigned.
OnSpawn
};
UENUM(BlueprintType)
enum class E〇〇AbilityActivationGroup : uint8
{
// Ability runs independently of all other abilities.
Independent,
// Ability is canceled and replaced by other exclusive abilities.
Exclusive_Replaceable,
// Ability blocks all other exclusive abilities from activating.
Exclusive_Blocking,
MAX UMETA(Hidden)
};
UCLASS(Abstract, HideCategories = Input, Meta = (ShortTooltip = "The base gameplay ability class used by this project."))
class TAGINPUTSAMPLE_API U〇〇GameplayAbility : public UGameplayAbility
{
GENERATED_BODY()
friend class U〇〇AbilitySystemComponent;
public:
U〇〇GameplayAbility(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
E〇〇AbilityActivationPolicy GetActivationPolicy() const { return ActivationPolicy; }
E〇〇AbilityActivationGroup GetActivationGroup() const { return ActivationGroup; }
void TryActivateAbilityOnSpawn(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) const;
void OnAbilityFailedToActivate(const FGameplayTagContainer& FailedReason) const
{
//NativeOnAbilityFailedToActivate(FailedReason);
ScriptOnAbilityFailedToActivate(FailedReason);
}
protected:
// Called when the ability fails to activate
UFUNCTION(BlueprintImplementableEvent)
void ScriptOnAbilityFailedToActivate(const FGameplayTagContainer& FailedReason) const;
//~UGameplayAbility interface
virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const override;
virtual void SetCanBeCanceled(bool bCanBeCanceled) override;
virtual void OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override;
virtual void OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override;
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override;
//~End of UGameplayAbility interface
virtual void OnPawnAvatarSet();
/** Called when this ability is granted to the ability system component. */
UFUNCTION(BlueprintImplementableEvent, Category = Ability, DisplayName = "OnAbilityAdded")
void K2_OnAbilityAdded();
/** Called when this ability is removed from the ability system component. */
UFUNCTION(BlueprintImplementableEvent, Category = Ability, DisplayName = "OnAbilityRemoved")
void K2_OnAbilityRemoved();
/** Called when the ability system is initialized with a pawn avatar. */
UFUNCTION(BlueprintImplementableEvent, Category = Ability, DisplayName = "OnPawnAvatarSet")
void K2_OnPawnAvatarSet();
// Defines how this ability is meant to activate.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "〇〇|Ability Activation")
E〇〇AbilityActivationPolicy ActivationPolicy;
// Defines the relationship between this ability activating and other abilities activating.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "〇〇|Ability Activation")
E〇〇AbilityActivationGroup ActivationGroup;
};
[コード]〇〇GameplayAbility.cpp
#include "〇〇GameplayAbility.h"
#include "〇〇GameplayTags.h"
#include "AbilitySystemGlobals.h"
#include "〇〇AbilitySystemComponent.h"
U〇〇GameplayAbility::U〇〇GameplayAbility(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ReplicationPolicy = EGameplayAbilityReplicationPolicy::ReplicateNo;
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ClientOrServer;
ActivationPolicy = E〇〇AbilityActivationPolicy::OnInputTriggered;
ActivationGroup = E〇〇AbilityActivationGroup::Independent;
}
bool U〇〇GameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const
{
if (!ActorInfo || !ActorInfo->AbilitySystemComponent.IsValid())
{
return false;
}
if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags))
{
return false;
}
//@TODO Possibly remove after setting up tag relationships
U〇〇AbilitySystemComponent* 〇〇ASC = CastChecked<U〇〇AbilitySystemComponent>(ActorInfo->AbilitySystemComponent.Get());
if (〇〇ASC->IsActivationGroupBlocked(ActivationGroup))
{
if (OptionalRelevantTags)
{
OptionalRelevantTags->AddTag(〇〇GameplayTags::Ability_ActivateFail_ActivationGroup);
}
return false;
}
return true;
}
void U〇〇GameplayAbility::SetCanBeCanceled(bool bCanBeCanceled)
{
// The ability can not block canceling if it's replaceable.
if (!bCanBeCanceled && (ActivationGroup == E〇〇AbilityActivationGroup::Exclusive_Replaceable))
{
return;
}
Super::SetCanBeCanceled(bCanBeCanceled);
}
void U〇〇GameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
Super::OnGiveAbility(ActorInfo, Spec);
K2_OnAbilityAdded();
TryActivateAbilityOnSpawn(ActorInfo, Spec);
}
void U〇〇GameplayAbility::OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
K2_OnAbilityRemoved();
Super::OnRemoveAbility(ActorInfo, Spec);
}
void U〇〇GameplayAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
}
void U〇〇GameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
bool U〇〇GameplayAbility::DoesAbilitySa〇〇fyTagRequirements(const UAbilitySystemComponent& AbilitySystemComponent, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const
{
// Specialized version to handle death exclusion and AbilityTags expansion via ASC
bool bBlocked = false;
bool bMissing = false;
UAbilitySystemGlobals& AbilitySystemGlobals = UAbilitySystemGlobals::Get();
const FGameplayTag& BlockedTag = AbilitySystemGlobals.ActivateFailTagsBlockedTag;
const FGameplayTag& MissingTag = AbilitySystemGlobals.ActivateFailTagsMissingTag;
// Check if any of this ability's tags are currently blocked
if (AbilitySystemComponent.AreAbilityTagsBlocked(AbilityTags))
{
bBlocked = true;
}
const U〇〇AbilitySystemComponent* 〇〇ASC = Cast<U〇〇AbilitySystemComponent>(&AbilitySystemComponent);
static FGameplayTagContainer AllRequiredTags;
static FGameplayTagContainer AllBlockedTags;
AllRequiredTags = ActivationRequiredTags;
AllBlockedTags = ActivationBlockedTags;
// Expand our ability tags to add additional required/blocked tags
if (〇〇ASC)
{
〇〇ASC->GetAdditionalActivationTagRequirements(AbilityTags, AllRequiredTags, AllBlockedTags);
}
// Check to see the required/blocked tags for this ability
if (AllBlockedTags.Num() || AllRequiredTags.Num())
{
static FGameplayTagContainer AbilitySystemComponentTags;
AbilitySystemComponentTags.Reset();
AbilitySystemComponent.GetOwnedGameplayTags(AbilitySystemComponentTags);
if (AbilitySystemComponentTags.HasAny(AllBlockedTags))
{
if (OptionalRelevantTags && AbilitySystemComponentTags.HasTag(〇〇GameplayTags::Status_Death))
{
// If player is dead and was rejected due to blocking tags, give that feedback
OptionalRelevantTags->AddTag(〇〇GameplayTags::Ability_ActivateFail_IsDead);
}
bBlocked = true;
}
if (!AbilitySystemComponentTags.HasAll(AllRequiredTags))
{
bMissing = true;
}
}
if (SourceTags != nullptr)
{
if (SourceBlockedTags.Num() || SourceRequiredTags.Num())
{
if (SourceTags->HasAny(SourceBlockedTags))
{
bBlocked = true;
}
if (!SourceTags->HasAll(SourceRequiredTags))
{
bMissing = true;
}
}
}
if (TargetTags != nullptr)
{
if (TargetBlockedTags.Num() || TargetRequiredTags.Num())
{
if (TargetTags->HasAny(TargetBlockedTags))
{
bBlocked = true;
}
if (!TargetTags->HasAll(TargetRequiredTags))
{
bMissing = true;
}
}
}
if (bBlocked)
{
if (OptionalRelevantTags && BlockedTag.IsValid())
{
OptionalRelevantTags->AddTag(BlockedTag);
}
return false;
}
if (bMissing)
{
if (OptionalRelevantTags && MissingTag.IsValid())
{
OptionalRelevantTags->AddTag(MissingTag);
}
return false;
}
return true;
}
void U〇〇GameplayAbility::OnPawnAvatarSet()
{
K2_OnPawnAvatarSet();
}
void U〇〇GameplayAbility::TryActivateAbilityOnSpawn(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) const
{
const bool bIsPredicting = (Spec.ActivationInfo.ActivationMode == EGameplayAbilityActivationMode::Predicting);
// Try to activate if activation policy is on spawn.
if (ActorInfo && !Spec.IsActive() && !bIsPredicting && (ActivationPolicy == E〇〇AbilityActivationPolicy::OnSpawn))
{
UAbilitySystemComponent* ASC = ActorInfo->AbilitySystemComponent.Get();
const AActor* AvatarActor = ActorInfo->AvatarActor.Get();
// If avatar actor is torn off or about to die, don't try to activate until we get the new one.
if (ASC && AvatarActor && !AvatarActor->GetTearOff() && (AvatarActor->GetLifeSpan() <= 0.0f))
{
const bool bIsLocalExecution = (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::LocalPredicted) || (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::LocalOnly);
const bool bIsServerExecution = (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::ServerOnly) || (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::ServerInitiated);
const bool bClientShouldActivate = ActorInfo->IsLocallyControlled() && bIsLocalExecution;
const bool bServerShouldActivate = ActorInfo->IsNetAuthority() && bIsServerExecution;
if (bClientShouldActivate || bServerShouldActivate)
{
ASC->TryActivateAbility(Spec.Handle);
}
}
}
}
◆AbilitySysytemComponetを継承したクラスを作成し拡張する
前回作成した「〇〇AbilitySysytemComponet」の追記をしていきます。
[コード]〇〇AbilitySysytemComponet.h
#pragma once
#include "CoreMinimal.h"
#include "〇〇GameplayAbility.h"
#include "AbilitySystemComponent.h"
#include "NativeGameplayTags.h"
#include "〇〇AbilitySystemComponent.generated.h"
class AActor;
class UGameplayAbility;
class U〇〇AbilityTagRelationshipMapping;
class UObject;
struct FFrame;
struct FGameplayAbilityTargetDataHandle;
TAGINPUTSAMPLE_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Gameplay_AbilityInputBlocked);
UCLASS()
class TAGINPUTSAMPLE_API U〇〇AbilitySystemComponent : public UAbilitySystemComponent
{
GENERATED_BODY()
public:
U〇〇AbilitySystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
//~UActorComponent interface
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
//~End of UActorComponent interface
virtual void InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor) override;
typedef TFunctionRef<bool(const U〇〇GameplayAbility* 〇〇Ability, FGameplayAbilitySpecHandle Handle)> TShouldCancelAbilityFunc;
void CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility);
void CancelInputActivatedAbilities(bool bReplicateCancelAbility);
void AbilityInputTagPressed(const FGameplayTag& InputTag);
void AbilityInputTagReleased(const FGameplayTag& InputTag);
bool IsActivationGroupBlocked(E〇〇AbilityActivationGroup Group) const;
void AddAbilityToActivationGroup(E〇〇AbilityActivationGroup Group, U〇〇GameplayAbility* 〇〇Ability);
void ProcessAbilityInput(float DeltaTime, bool bGamePaused);
void ClearAbilityInput();
void RemoveAbilityFromActivationGroup(E〇〇AbilityActivationGroup Group, U〇〇GameplayAbility* 〇〇Ability);
void CancelActivationGroupAbilities(E〇〇AbilityActivationGroup Group, U〇〇GameplayAbility* Ignore〇〇Ability, bool bReplicateCancelAbility);
protected:
void TryActivateAbilitiesOnSpawn();
virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& Spec) override;
virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& Spec) override;
virtual void NotifyAbilityActivated(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability) override;
virtual void NotifyAbilityFailed(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason) override;
virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled) override;
virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags) override;
virtual void HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled) override;
/** Notify client that an ability failed to activate */
UFUNCTION(Client, Unreliable)
void ClientNotifyAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason);
void HandleAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason);
protected:
// Handles to abilities that had their input pressed this frame.
TArray<FGameplayAbilitySpecHandle> InputPressedSpecHandles;
// Handles to abilities that had their input released this frame.
TArray<FGameplayAbilitySpecHandle> InputReleasedSpecHandles;
// Handles to abilities that have their input held.
TArray<FGameplayAbilitySpecHandle> InputHeldSpecHandles;
// Number of abilities running in each activation group.
int32 ActivationGroupCounts[(uint8)E〇〇AbilityActivationGroup::MAX];
};
[コード]〇〇AbilitySysytemComponet.cpp
#include "〇〇AbilitySystemComponent.h"
#include "〇〇AbilityTagRelationshipMapping.h"
#include "〇〇GameplayAbility.h"
UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_AbilityInputBlocked, "Gameplay.AbilityInputBlocked");
U〇〇AbilitySystemComponent::U〇〇AbilitySystemComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
InputPressedSpecHandles.Reset();
InputReleasedSpecHandles.Reset();
InputHeldSpecHandles.Reset();
FMemory::Memset(ActivationGroupCounts, 0, sizeof(ActivationGroupCounts));
}
void U〇〇AbilitySystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
}
void U〇〇AbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor)
{
FGameplayAbilityActorInfo* ActorInfo = AbilityActorInfo.Get();
check(ActorInfo);
check(InOwnerActor);
const bool bHasNewPawnAvatar = Cast<APawn>(InAvatarActor) && (InAvatarActor != ActorInfo->AvatarActor);
Super::InitAbilityActorInfo(InOwnerActor, InAvatarActor);
if (bHasNewPawnAvatar)
{
// Notify all abilities that a new pawn avatar has been set
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
U〇〇GameplayAbility* 〇〇AbilityCDO = CastChecked<U〇〇GameplayAbility>(AbilitySpec.Ability);
if (〇〇AbilityCDO->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced)
{
TArray<UGameplayAbility*> Instances = AbilitySpec.GetAbilityInstances();
for (UGameplayAbility* AbilityInstance : Instances)
{
U〇〇GameplayAbility* 〇〇AbilityInstance = Cast<U〇〇GameplayAbility>(AbilityInstance);
if (〇〇AbilityInstance)
{
// Ability instances may be missing for replays
〇〇AbilityInstance->OnPawnAvatarSet();
}
}
}
else
{
〇〇AbilityCDO->OnPawnAvatarSet();
}
}
TryActivateAbilitiesOnSpawn();
}
}
void U〇〇AbilitySystemComponent::TryActivateAbilitiesOnSpawn()
{
ABILITYLIST_SCOPE_LOCK();
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
const U〇〇GameplayAbility* 〇〇AbilityCDO = CastChecked<U〇〇GameplayAbility>(AbilitySpec.Ability);
〇〇AbilityCDO->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), AbilitySpec);
}
}
void U〇〇AbilitySystemComponent::CancelInputActivatedAbilities(bool bReplicateCancelAbility)
{
auto ShouldCancelFunc = [this](const U〇〇GameplayAbility* 〇〇Ability, FGameplayAbilitySpecHandle Handle)
{
const E〇〇AbilityActivationPolicy ActivationPolicy = 〇〇Ability->GetActivationPolicy();
return ((ActivationPolicy == E〇〇AbilityActivationPolicy::OnInputTriggered) || (ActivationPolicy == E〇〇AbilityActivationPolicy::WhileInputActive));
};
CancelAbilitiesByFunc(ShouldCancelFunc, bReplicateCancelAbility);
}
void U〇〇AbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& InputTag)
{
if (InputTag.IsValid())
{
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
if (AbilitySpec.Ability && (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag)))
{
InputPressedSpecHandles.AddUnique(AbilitySpec.Handle);
InputHeldSpecHandles.AddUnique(AbilitySpec.Handle);
}
}
}
}
void U〇〇AbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& InputTag)
{
if (InputTag.IsValid())
{
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
if (AbilitySpec.Ability && (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag)))
{
InputReleasedSpecHandles.AddUnique(AbilitySpec.Handle);
InputHeldSpecHandles.Remove(AbilitySpec.Handle);
}
}
}
}
void U〇〇AbilitySystemComponent::ProcessAbilityInput(float DeltaTime, bool bGamePaused)
{
if (HasMatchingGameplayTag(TAG_Gameplay_AbilityInputBlocked))
{
ClearAbilityInput();
return;
}
static TArray<FGameplayAbilitySpecHandle> AbilitiesToActivate;
AbilitiesToActivate.Reset();
//@TODO: See if we can use FScopedServerAbilityRPCBatcher ScopedRPCBatcher in some of these loops
//
// Process all abilities that activate when the input is held.
//
for (const FGameplayAbilitySpecHandle& SpecHandle : InputHeldSpecHandles)
{
if (const FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle))
{
if (AbilitySpec->Ability && !AbilitySpec->IsActive())
{
const U〇〇GameplayAbility* 〇〇AbilityCDO = CastChecked<U〇〇GameplayAbility>(AbilitySpec->Ability);
if (〇〇AbilityCDO->GetActivationPolicy() == E〇〇AbilityActivationPolicy::WhileInputActive)
{
AbilitiesToActivate.AddUnique(AbilitySpec->Handle);
}
}
}
}
//
// Process all abilities that had their input pressed this frame.
//
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 U〇〇GameplayAbility* 〇〇AbilityCDO = CastChecked<U〇〇GameplayAbility>(AbilitySpec->Ability);
if (〇〇AbilityCDO->GetActivationPolicy() == E〇〇AbilityActivationPolicy::OnInputTriggered)
{
AbilitiesToActivate.AddUnique(AbilitySpec->Handle);
}
}
}
}
}
//
// Try to activate all the abilities that are from presses and holds.
// We do it all at once so that held inputs don't activate the ability
// and then also send a input event to the ability because of the press.
//
for (const FGameplayAbilitySpecHandle& AbilitySpecHandle : AbilitiesToActivate)
{
UE_LOG(LogTemp, Display, TEXT("U〇〇AbilitySystemComponent::ProcessAbilityInput->TryActivateAbility"));
TryActivateAbility(AbilitySpecHandle);
}
//
// Process all abilities that had their input released this frame.
//
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);
}
}
}
}
//
// Clear the cached ability handles.
//
InputPressedSpecHandles.Reset();
InputReleasedSpecHandles.Reset();
}
void U〇〇AbilitySystemComponent::ClearAbilityInput()
{
InputPressedSpecHandles.Reset();
InputReleasedSpecHandles.Reset();
InputHeldSpecHandles.Reset();
}
void U〇〇AbilitySystemComponent::NotifyAbilityActivated(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability)
{
Super::NotifyAbilityActivated(Handle, Ability);
U〇〇GameplayAbility* 〇〇Ability = CastChecked<U〇〇GameplayAbility>(Ability);
AddAbilityToActivationGroup(〇〇Ability->GetActivationGroup(), 〇〇Ability);
}
void U〇〇AbilitySystemComponent::NotifyAbilityFailed(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason)
{
Super::NotifyAbilityFailed(Handle, Ability, FailureReason);
if (APawn* Avatar = Cast<APawn>(GetAvatarActor()))
{
if (!Avatar->IsLocallyControlled() && Ability->IsSupportedForNetworking())
{
ClientNotifyAbilityFailed(Ability, FailureReason);
return;
}
}
HandleAbilityFailed(Ability, FailureReason);
}
void U〇〇AbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled)
{
Super::NotifyAbilityEnded(Handle, Ability, bWasCancelled);
U〇〇GameplayAbility* 〇〇Ability = CastChecked<U〇〇GameplayAbility>(Ability);
RemoveAbilityFromActivationGroup(〇〇Ability->GetActivationGroup(), 〇〇Ability);
}
void U〇〇AbilitySystemComponent::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 U〇〇AbilitySystemComponent::HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled)
{
Super::HandleChangeAbilityCanBeCanceled(AbilityTags, RequestingAbility, bCanBeCanceled);
//@TODO: Apply any special logic like blocking input or movement
}
void U〇〇AbilitySystemComponent::AbilitySpecInputPressed(FGameplayAbilitySpec& Spec)
{
Super::AbilitySpecInputPressed(Spec);
// We don't support UGameplayAbility::bReplicateInputDirectly.
// Use replicated events instead so that the WaitInputPress ability task works.
if (Spec.IsActive())
{
// Invoke the InputPressed event. This is not replicated here. If someone is listening, they may replicate the InputPressed event to the server.
InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey());
}
}
void U〇〇AbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& Spec)
{
Super::AbilitySpecInputReleased(Spec);
// We don't support UGameplayAbility::bReplicateInputDirectly.
// Use replicated events instead so that the WaitInputRelease ability task works.
if (Spec.IsActive())
{
// Invoke the InputReleased event. This is not replicated here. If someone is listening, they may replicate the InputReleased event to the server.
InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey());
}
}
bool U〇〇AbilitySystemComponent::IsActivationGroupBlocked(E〇〇AbilityActivationGroup Group) const
{
bool bBlocked = false;
switch (Group)
{
case E〇〇AbilityActivationGroup::Independent:
// Independent abilities are never blocked.
bBlocked = false;
break;
case E〇〇AbilityActivationGroup::Exclusive_Replaceable:
case E〇〇AbilityActivationGroup::Exclusive_Blocking:
// Exclusive abilities can activate if nothing is blocking.
bBlocked = (ActivationGroupCounts[(uint8)E〇〇AbilityActivationGroup::Exclusive_Blocking] > 0);
break;
default:
checkf(false, TEXT("IsActivationGroupBlocked: Invalid ActivationGroup [%d]\n"), (uint8)Group);
break;
}
return bBlocked;
}
void U〇〇AbilitySystemComponent::ClientNotifyAbilityFailed_Implementation(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason)
{
HandleAbilityFailed(Ability, FailureReason);
}
void U〇〇AbilitySystemComponent::HandleAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason)
{
if (const U〇〇GameplayAbility* 〇〇Ability = Cast<const U〇〇GameplayAbility>(Ability))
{
〇〇Ability->OnAbilityFailedToActivate(FailureReason);
}
}
void U〇〇AbilitySystemComponent::AddAbilityToActivationGroup(E〇〇AbilityActivationGroup Group, U〇〇GameplayAbility* 〇〇Ability)
{
check(〇〇Ability);
check(ActivationGroupCounts[(uint8)Group] < INT32_MAX);
ActivationGroupCounts[(uint8)Group]++;
const bool bReplicateCancelAbility = false;
switch (Group)
{
case E〇〇AbilityActivationGroup::Independent:
// Independent abilities do not cancel any other abilities.
break;
case E〇〇AbilityActivationGroup::Exclusive_Replaceable:
case E〇〇AbilityActivationGroup::Exclusive_Blocking:
CancelActivationGroupAbilities(E〇〇AbilityActivationGroup::Exclusive_Replaceable, 〇〇Ability, bReplicateCancelAbility);
break;
default:
checkf(false, TEXT("AddAbilityToActivationGroup: Invalid ActivationGroup [%d]\n"), (uint8)Group);
break;
}
const int32 ExclusiveCount = ActivationGroupCounts[(uint8)E〇〇AbilityActivationGroup::Exclusive_Replaceable] + ActivationGroupCounts[(uint8)E〇〇AbilityActivationGroup::Exclusive_Blocking];
if (!ensure(ExclusiveCount <= 1))
{
UE_LOG(LogTemp, Error, TEXT("AddAbilityToActivationGroup: Multiple exclusive abilities are running."));
}
}
void U〇〇AbilitySystemComponent::RemoveAbilityFromActivationGroup(E〇〇AbilityActivationGroup Group, U〇〇GameplayAbility* 〇〇Ability)
{
check(〇〇Ability);
check(ActivationGroupCounts[(uint8)Group] > 0);
ActivationGroupCounts[(uint8)Group]--;
}
void U〇〇AbilitySystemComponent::CancelActivationGroupAbilities(E〇〇AbilityActivationGroup Group, U〇〇GameplayAbility* Ignore〇〇Ability, bool bReplicateCancelAbility)
{
auto ShouldCancelFunc = [this, Group, Ignore〇〇Ability](const U〇〇GameplayAbility* 〇〇Ability, FGameplayAbilitySpecHandle Handle)
{
return ((〇〇Ability->GetActivationGroup() == Group) && (〇〇Ability != Ignore〇〇Ability));
};
CancelAbilitiesByFunc(ShouldCancelFunc, bReplicateCancelAbility);
}
void U〇〇AbilitySystemComponent::CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility)
{
ABILITYLIST_SCOPE_LOCK();
for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items)
{
if (!AbilitySpec.IsActive())
{
continue;
}
U〇〇GameplayAbility* 〇〇AbilityCDO = CastChecked<U〇〇GameplayAbility>(AbilitySpec.Ability);
if (〇〇AbilityCDO->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced)
{
// Cancel all the spawned instances, not the CDO.
TArray<UGameplayAbility*> Instances = AbilitySpec.GetAbilityInstances();
for (UGameplayAbility* AbilityInstance : Instances)
{
U〇〇GameplayAbility* 〇〇AbilityInstance = CastChecked<U〇〇GameplayAbility>(AbilityInstance);
if (ShouldCancelFunc(〇〇AbilityInstance, AbilitySpec.Handle))
{
if (〇〇AbilityInstance->CanBeCanceled())
{
〇〇AbilityInstance->CancelAbility(AbilitySpec.Handle, AbilityActorInfo.Get(), 〇〇AbilityInstance->GetCurrentActivationInfo(), bReplicateCancelAbility);
}
else
{
UE_LOG(LogTemp, Error, TEXT("CancelAbilitiesByFunc: Can't cancel ability [%s] because CanBeCanceled is false."), *〇〇AbilityInstance->GetName());
}
}
}
}
else
{
// Cancel the non-instanced ability CDO.
if (ShouldCancelFunc(〇〇AbilityCDO, AbilitySpec.Handle))
{
// Non-instanced abilities can always be canceled.
check(〇〇AbilityCDO->CanBeCanceled());
〇〇AbilityCDO->CancelAbility(AbilitySpec.Handle, AbilityActorInfo.Get(), FGameplayAbilityActivationInfo(), bReplicateCancelAbility);
}
}
}
}
ここまで作ってエラーを出ないように諸々調整してください。
◆CharacterにGameplayAbilitySysytemの実装をする
上記のGameplayAbility周りのコードを実装し、コンパイルをして通るようになったら
Characterに対してGameplayAbility周りの実装をしていきます。
[コード]〇〇Character.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "〇〇AbilitySystemComponent.h"
#include "AbilitySystemInterface.h"
#include "TagInputSampleCharacter.generated.h"
class USpringArmComponent;
class UCameraComponent;
class UInputMappingContext;
class UInputAction;
class U〇〇InputConfig;
class U〇〇AbilitySet;
struct FInputActionValue;
DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);
UCLASS(config=Game)
class ATagInputSampleCharacter : public ACharacter, public IAbilitySystemInterface
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
USpringArmComponent* CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
UCameraComponent* FollowCamera;
/** MappingContext */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputMappingContext* DefaultMappingContext;
/** InputConfig */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
U〇〇InputConfig* DefaultInputConfig;
/** AbilitySet 新しくAbilitySetのデータをBlueprintから参照できるようにします */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
U〇〇AbilitySet* DefaultAbilitySet;
public:
ATagInputSampleCharacter();
// AbilitySystemコンポーネント
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Abilities, meta = (AllowPrivateAccess = "true"))
class U〇〇AbilitySystemComponent* AbilitySystemComponent;
U〇〇AbilitySystemComponent* GetAbilitySystemComponent() const {
return AbilitySystemComponent;
};
//~AActor interface
virtual void PreInitializeComponents() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Reset() override;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
virtual void PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker) override;
//~End of AActor interface
//~APawn interface
virtual void NotifyControllerChanged() override;
//~End of APawn interface
protected:
/** Called for movement input */
void Move(const FInputActionValue& Value);
// アビリティのボタンが押されたとき、ボタンを離したときに呼びだす関数
void Input_AbilityInputTagPressed(FGameplayTag InputTag);
void Input_AbilityInputTagReleased(FGameplayTag InputTag);
protected:
//アビリティシステムが初期化、削除されたときに呼ばれる(なくても大丈夫)
virtual void OnAbilitySystemInitialized();
virtual void OnAbilitySystemUninitialized();
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// To add mapping context
virtual void BeginPlay();
//入力の受付を監視するためにTickを入れる
virtual void Tick(float DeltaTime);
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};
[コード]〇〇Character.cpp
#include "TagInputSampleCharacter.h"
#include "Engine/LocalPlayer.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/Controller.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Input/〇〇InputComponent.h"
#include "InputActionValue.h"
#include "〇〇GameplayTags.h"
#include "AbilitySystem/〇〇AbilitySet.h"
DEFINE_LOG_CATEGORY(LogTemplateCharacter);
//////////////////////////////////////////////////////////////////////////
// ATagInputSampleCharacter
ATagInputSampleCharacter::ATagInputSampleCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate
// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
// instead of recompiling to adjust them
GetCharacterMovement()->JumpZVelocity = 700.f;
GetCharacterMovement()->AirControl = 0.35f;
GetCharacterMovement()->MaxWalkSpeed = 500.f;
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 400.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
// アビリティシステムコンポーネントをここで定義する
AbilitySystemComponent = CreateDefaultSubobject<U〇〇AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->Se〇〇Replicated(true);
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
PrimaryActorTick.bCanEverTick = true;
}
void ATagInputSampleCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
//BlueprintにセットされているAbilitySetがあれば読み込む
if (DefaultAbilitySet)
{
DefaultAbilitySet->GiveToAbilitySystem(AbilitySystemComponent, nullptr);
}
}
void ATagInputSampleCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
//アビリティの受付を監視する(本来は違う場所だが、その場所が今はないのでここでやる)
AbilitySystemComponent->ProcessAbilityInput(DeltaTime, false);
}
//////////////////////////////////////////////////////////////////////////
// Input
void ATagInputSampleCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
if (U〇〇InputComponent* 〇〇IC = Cast<U〇〇InputComponent>(PlayerInputComponent))
{
〇〇IC->AddInputMappings(DefaultInputConfig, Subsystem);
//アビリティのバインドハンドル
TArray<uint32> BindHandles;
//アビリティのバインドをする
〇〇IC->BindAbilityActions(DefaultInputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles);
〇〇IC->BindNativeAction(DefaultInputConfig, 〇〇GameplayTags::InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Move, /*bLogIfNotFound=*/ false);
}
else
{
UE_LOG(LogTemplateCharacter, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
}
}
}
}
void ATagInputSampleCharacter::Move(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller != nullptr)
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void ATagInputSampleCharacter::Input_AbilityInputTagPressed(FGameplayTag InputTag)
{
AbilitySystemComponent->AbilityInputTagPressed(InputTag);
}
void ATagInputSampleCharacter::Input_AbilityInputTagReleased(FGameplayTag InputTag)
{
AbilitySystemComponent->AbilityInputTagReleased(InputTag);
}
void ATagInputSampleCharacter::PreInitializeComponents()
{
Super::PreInitializeComponents();
}
void ATagInputSampleCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
}
void ATagInputSampleCharacter::Reset()
{
}
void ATagInputSampleCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
}
void ATagInputSampleCharacter::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
}
void ATagInputSampleCharacter::NotifyControllerChanged()
{
Super::NotifyControllerChanged();
}
void ATagInputSampleCharacter::OnAbilitySystemInitialized()
{
}
void ATagInputSampleCharacter::OnAbilitySystemUninitialized()
{
}
【〇〇Character.h】
・AbilitySysytemComponetを定義
・AbilitySetをブループリントから設定できるように定義
・ボタンを押された、離された時に通る処理を定義
・Tickにて入力されたアビリティを処理するための関数を定義
【〇〇Character.cpp】
・コンストラクタでAbilitySysytemComponetを初期化
・BeginPlayでAbilitySetからAbilityをプレイヤーに付与
・SetupPlayerInputComponentでAbilityのアクションをバインド
・Tickでアビリティの入力を監視
・Input_AbilityInputTagPressed、Input_AbilityInputTagReleasedで入力が来た時にタグをAbilityに知らせる
長々とソースコードばかり書いてしまった回ですが、ここまでの処理がエラーなく通れば
Abilityを発行できる状態になります。
◆JumpのAbilityを実装する
実際にJumpのアビリティを作成します。
ブループリントを作成し、クラス「〇〇GameplayAbility」を探し継承します。
作成されたブループリントは「GA_Jump」と名付けておきます
※GameplayAbilityは接頭詞にしばしば「GA_」が使われます。
↓
作ったブループリントの親クラスが「〇〇GameplayAbility」になっていることを確認してください。
![](https://assets.st-note.com/img/1700789094802-KWnjzB3D3H.png?width=1200)
次に、ブループリントの処理を作ります。以下の画像のように作ってみてください。
※細かいことは端折りますが、アビリティが実行されると「Event ActivateAbility」が発行されて処理が走ります。
![](https://assets.st-note.com/img/1700789128539-2FRPAaCK5d.png?width=1200)
処理の最後は必ず「EndAbility」を呼んでください。
呼ばないとアビリティが終わったことにならず、アビリティを発行後うまく動かなくなります。(そのゲーム内だけの挙動です)
このファイルを作ったら、AbilitySetに設定します。
![](https://assets.st-note.com/img/1700789180010-KYVi5405Ug.png?width=1200)
全てを保存して、ゲームを実行します。
上記処理がすべてうまく機能していれば、JumpボタンをクリックするとGA_Jumpがアクティベートされて、グレイマンがジャンプするようになります。
後は同じような方法で
・タグを作成
・タグに紐づいたアビリティを作成
・入力の設定
・AbilitySetに設定
この手順だけで、アビリティを実装できるようになります。
Abilityをブループリントで作る場合に限っては、C++をこれ以上記述しなくても機能を着脱できるようになります。
かなり長くなってしまいましたが、LyraのInputTagによるGameplayAbilityの紐づけ部分の処理ができるようになりました。
※あくまでアビリティが動くようになっただけなので、エフェクト、アトリビュートはまだ動作しません。
※Lyraには他にも多数の機能があり、この機能はその一端を移植しただけになります。
この機能をベースに今後も少しずつ拡張をしていくような記事を作っていければと思いますので、また見かけた際は流し見でもいいので見てもらえればと思います。
この記事が気に入ったらサポートをしてみませんか?