- Gameplay Ability System コードリーディング ① WaitOverlap - You are done!
- Gameplay Ability System コードリーディング ② SpawnActor - You are done!
- Gameplay Ability System コードリーディング ③ WaitTargetData - You are done!
上記の続きになります。
WaitTargetData
の表層部分だけを前回ざっと眺めました。
WaitTargetData は GameplayAbilityTargetActor で挙動が変わる
上記は左が AGameplayAbilityTargetActor_Radius
を指定し、右が AGameplayAbilityTargetActor_SingleLineTrace
を指定したものですが、指定した GameplayAbilityTargetActor
によって入力ピンも大きく変化しているのが分かります。
SpawnActor
のところでも貼りましたが AbilityTask.h の説明を再度貼ります。
We have additional support for AbilityTasks that want to spawn actors. Though this could be accomplished in an Activate() function, it would not be possible to pass in dynamic "ExposeOnSpawn" actor properties. This is a powerful feature of blueprints, in order to support this, you need to implement a different step 3:
Instead of an Activate() function, you should implement a BeginSpawningActor() and FinishSpawningActor() function.
つまり、WaitTargetData
のノードにそこで指定した GameplayAbilityTargetActor
のプロパティが出ている (ExposeOnSpawn
) のは、これに則って実装しているからということだと分かります。
よって、例えば自身で spawn する状況があるとすれば以下の AGameplayAbilityTargetActor_Radius
の例のようになり、プロパティは同じことが分かります。 (Transform に関しては、SpawnActorDeferred
で指定されているため、ノードには出ていません)
GASDocumentation の説明
4.11.2 Target Actors | GASDocumentation/README.jp.md at lang-ja · sentyaanko/GASDocumentation
恣意的にはなってしまいますが、読み進めるヒントになりそうな分かりやすい部分だけ引用します。
GameplayAbilities
は、WaitTargetData
AbilityTask
を伴ってTargetActors
を視覚化のためにスポーンし、ワールドからのターゲット情報をキャプチャします。~略
TargetActors
はAActor
に基づいており、ターゲットとする 場所 と 方法 を示すために、あらゆる種類の可視コンポーネントを持つことができます(スタティックメッシュやデカールのような)。~略
これらは、「基本のトレースまたはオーバーラップを使いターゲットの情報をキャプチャ」し、「
TargetActor
の実装に基づいて、結果をFHitResults
またはAActor
の配列としてTargetData
に変換」します。
TargetActors
AGameplayAbilityTargetActor
をスポーンしているのは、すでに WaitTargetData
を読み進めた部分で確認できています。
以下は GameplayAbilityTargetActor.h
の class 定義部分のみですが、たしかに AActor
に基づいています。
UCLASS(Blueprintable, abstract, notplaceable) class GAMEPLAYABILITIES_API AGameplayAbilityTargetActor : public AActor
AGameplayAbilityTargetActor
は abstract なので、ここからは一つ TargetActor を選んで見ていきたいと思います。
AGameplayAbilityTargetActor_Radius
Built-in の GameplayAbilityTargetActor のうち、Radius は特に他との継承関係もないため読みやすいだろうと判断してまずはこれを見てみます。
ヘッダファイル
UCLASS(Blueprintable, notplaceable) class GAMEPLAYABILITIES_API AGameplayAbilityTargetActor_Radius : public AGameplayAbilityTargetActor { GENERATED_UCLASS_BODY() public: virtual void StartTargeting(UGameplayAbility* Ability) override; virtual void ConfirmTargetingAndContinue() override; /** Radius of target acquisition around the ability's start location. */ UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (ExposeOnSpawn = true), Category = Radius) float Radius; protected: TArray<TWeakObjectPtr<AActor> > PerformOverlap(const FVector& Origin); FGameplayAbilityTargetDataHandle MakeTargetData(const TArray<TWeakObjectPtr<AActor>>& Actors, const FVector& Origin) const; };
まず目に入るのが Radius
プロパティです。 ExposeOnSpawn = true
となっており、実際にノードにも表出しています。
一方で、それ以外には ExposeOnSpawn = true
のプロパティはありません、つまり他は基底クラスである AGameplayAbilityTargetActor
にあるということです。
AGameplayAbilityTargetActor のヘッダファイル
class GAMEPLAYABILITIES_API AGameplayAbilityTargetActor : public AActor { public: /** Describes where the targeting action starts, usually the player character or a socket on the player character. */ //UPROPERTY(BlueprintReadOnly, meta=(ExposeOnSpawn=true), Category=Targeting) UPROPERTY(BlueprintReadOnly, meta = (ExposeOnSpawn = true), Replicated, Category = Targeting) FGameplayAbilityTargetingLocationInfo StartLocation; /** Parameters for world reticle. Usage of these parameters is dependent on the reticle. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) FWorldReticleParameters ReticleParams; /** Reticle that will appear on top of acquired targets. Reticles will be spawned/despawned as targets are acquired/lost. */ UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (ExposeOnSpawn = true), Category = Targeting) TSubclassOf<AGameplayAbilityWorldReticle> ReticleClass; //Using a special class for replication purposes. UPROPERTY(BlueprintReadWrite, Replicated, meta = (ExposeOnSpawn = true), Category = Targeting) FGameplayTargetDataFilterHandle Filter; /** Draw the debug information (if applicable) for this targeting actor. */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Replicated, meta = (ExposeOnSpawn = true), Category = Targeting) bool bDebug;
上記に ExposeOnSpawn = true
のものだけ貼りましたが、これでノードに出ているプロパティは全て揃っています。
ここでは個々の詳細には触れませんが、GASDocumentation に解説があります。
- 4.11.3 Target Data Filters | GASDocumentation/README.jp.md at lang-ja · sentyaanko/GASDocumentation
- 4.11.4 Gameplay Ability World Reticles (Gameplay Ability ワールド焦点板) | GASDocumentation/README.jp.md at lang-ja · sentyaanko/GASDocumentation
ソースファイル
初期化
AGameplayAbilityTargetActor_Radius::AGameplayAbilityTargetActor_Radius(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.TickGroup = TG_PrePhysics; ShouldProduceTargetDataOnServer = true; }
初期化部分です。アクターとして普通に spawn され Tick も有効にされるようです。
ShouldProduceTargetDataOnServer
に関してですが、基底クラスの方に定義があります。
/** The TargetData this class produces can be entirely generated on the server. We don't require the client to send us full or partial TargetData (possibly just a 'confirm') */ UPROPERTY(EditAnywhere, Category=Advanced) bool ShouldProduceTargetDataOnServer;
少し長いですが GASDocumentation の方に解説があるので引用します。
TargetActors
はデフォルトではレプリケーションされません : しかしながら、ローカルプレイヤーがどこをターゲティングしているかを他のプレイヤーが見えるのがゲームとして理にかなっているのであれば、レプリケーションを作るようにすることもできます。 それらにはWaitTargetData
AbilityTask
上の RPCs を介してサーバーと通信するためのデフォルトの機能が含まれています。TargetActor
のShouldProduceTargetDataOnServer
プロパティがfalse
に設定されると、クライアントはUAbilityTask_WaitTargetData::OnTargetDataReadyCallback()
のCallServerSetReplicatedTargetData()
を介した確認時にTargetData
をサーバーに RPC します。ShouldProduceTargetDataOnServer
がtrue
ならば、クライアントは汎用確認イベントEAbilityGenericReplicatedEvent::GenericConfirm
を送り、UAbilityTask_WaitTargetData::OnTargetDataReadyCallback()
でサーバーに RPC し、サーバーは受信した RPC の上で、サーバー上でデータを生成するためにトレースまたはオーバーラップのチェックを行います。 クライアントがターゲティングをキャンセルした場合、汎用的なキャンセルイベントEAbilityGenericReplicatedEvent::GenericCancel
を送り、UAbilityTask_WaitTargetData::OnTargetDataCancelledCallback
でサーバーに RPC します。
雑な解釈になってしまいますが ShouldProduceTargetDataOnServer
は「サーバー側で TargetData
を生成するべきか」というフラグであり、 true
ならばサーバーには「サーバー側で改めてトレースなりして生成しろ」、false
の場合は、「クライアントで生成した TargetData
を送るね」ということになるのだと思います。
(利用している callback が異なっているようなので、また後ほど詳しく見てみます 🙏)
そして、ここでは true
が設定されているということです。
AGameplayAbilityTargetActor::AGameplayAbilityTargetActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { ShouldProduceTargetDataOnServer = false; bDebug = false; bDestroyOnConfirmation = true; }
ちなみに、基底クラス側では false
が指定されているためデフォルトでは false
のようです。
StartTargeting
AbilityTaskWaitTargetData
の方で、この TargetActor の Finalize 処理で呼び出されていた関数です。
void AGameplayAbilityTargetActor_Radius::StartTargeting(UGameplayAbility* InAbility) { Super::StartTargeting(InAbility); SourceActor = InAbility->GetCurrentActorInfo()->AvatarActor.Get(); } // 基底クラス側 void AGameplayAbilityTargetActor::StartTargeting(UGameplayAbility* Ability) { OwningAbility = Ability; }
特に特筆する点はありません。以降で使われる変数に値を設定しています。
ConfirmTargetingAndContinue
この AGameplayAbilityTargetActor_Radius
はこの ConfirmTargetingAndContinue
が実質の処理になります。
初期化で Tick は有効になっていますが、Tick の実装はなく、この ConfirmTargetingAndContinue
が実行された時点で終了します。
この関数が呼び出されるタイミングはいくつかありますが、ここで触れると長くなるのでひとまず以下のドキュメントを貼るに留めます。EGameplayTargetingConfirmation::Type
の説明の表で確認できます。
4.11.2 Target Actors | GASDocumentation/README.jp.md at lang-ja · sentyaanko/GASDocumentation
void AGameplayAbilityTargetActor_Radius::ConfirmTargetingAndContinue() { check(ShouldProduceTargetData()); if (SourceActor) { FVector Origin = StartLocation.GetTargetingTransform().GetLocation(); FGameplayAbilityTargetDataHandle Handle = MakeTargetData(PerformOverlap(Origin), Origin); TargetDataReadyDelegate.Broadcast(Handle); } }
まず、if (SourceActor)
に関してですが、これは StartTargeting
で設定済みのため、通常は真になると考えられます。
つまり、Confirm が行われれば、必ず if
の内部に入るということです。
以降は、入力ピンになっている StartLocation
の Location を Origin として、 TargetDataHandle
を生成し、TargetDataReadyDelegate
delegate に流しているようです。
TargetDataReadyDelegate
は AbilityTask_WaitTargetData#InitializeTargetActor
で AbilityTask_WaitTargetData#OnTargetDataReadyCallback
に紐付けられているので、ここでの delegate はそのまま WaitTargetData
の ValidData
(出力ピン) の delegate に繋がります。
よって、この Radius タスクは、「WaitTargetData
の実行後、 Confirm を待機 (Wait) し、Confirm された時点で StartLocation
に基づいてTargetData
データを即生成し渡す」という形になります。
MakeTargetData / PerformOverlap
TargetDataHandle
を生成している部分を見ていきます。
FGameplayAbilityTargetDataHandle AGameplayAbilityTargetActor_Radius::MakeTargetData(const TArray<TWeakObjectPtr<AActor>>& Actors, const FVector& Origin) const { if (OwningAbility) { /** Use the source location instead of the literal origin */ return StartLocation.MakeTargetDataHandleFromActors(Actors, false); } return FGameplayAbilityTargetDataHandle(); } TArray<TWeakObjectPtr<AActor> > AGameplayAbilityTargetActor_Radius::PerformOverlap(const FVector& Origin) { bool bTraceComplex = false; FCollisionQueryParams Params(SCENE_QUERY_STAT(RadiusTargetingOverlap), bTraceComplex); Params.bReturnPhysicalMaterial = false; TArray<FOverlapResult> Overlaps; SourceActor->GetWorld()->OverlapMultiByObjectType(Overlaps, Origin, FQuat::Identity, FCollisionObjectQueryParams(ECC_Pawn), FCollisionShape::MakeSphere(Radius), Params); TArray<TWeakObjectPtr<AActor>> HitActors; for (int32 i = 0; i < Overlaps.Num(); ++i) { //Should this check to see if these pawns are in the AimTarget list? APawn* PawnActor = Overlaps[i].OverlapObjectHandle.FetchActor<APawn>(); if (PawnActor && !HitActors.Contains(PawnActor) && Filter.FilterPassesForActor(PawnActor)) { HitActors.Add(PawnActor); } } return HitActors; }
まず PerformOverlap
ですが、OverlapMultiByObjectType
で Origin
(実際には StartLocation
) を Center にして 入力値の Radius (半径) を指定した Sphere Collision で Pawn
を取得しています。
- UWorld::OverlapMultiByObjectType | Unreal Engine Documentation
- FCollisionShape::MakeSphere | Unreal Engine Documentation
その後は Filter に pass したもののみ HitActors
に追加し、そのまま return しています。
なので、そのまま空の配列が return されることもありえるようです。
続いて MakeTargetData
ですが、入力の StartLocation
から TargetDataHandle を生成し、その引数に PerformOverlap
で得た配列を渡しています。 (StartLocation から TargetDataHandle を生成しているのはパッとはピンとこないのですが、中を見ると SourceLocation
として Handle の方に渡しているようです。)
まとめ
AGameplayAbilityTargetActor
と WaitTargetData
の関係、そして簡単そうな例として AGameplayAbilityTargetActor_Radius
をざっと眺めました。
どうやら Radius は Confirm を Wait するサンプルとして参考になりそうですが、肝心の Confirm 部分を飛ばした上に、実際の挙動が微妙に想像しにくいです。
次回は Confirm 部分を確認しつつ、実際にこの Radius を利用した BP を書いてみたいと思います。