Gameplay Ability System コードリーディング ① WaitOverlap - You are done!
①では簡単な WaitOverlap
を見ましたが、今回はそれよりも複雑(らしい)WaitTargetData
を見ていきたいと思いました、が。
WaitTargetData に行く前に
前回確認した AbilityTask
の基本要項にあったものに以下がありました。
Implement a Activate() function (defined here in base class). This function should actually start/execute your task logic. It is safe to invoke callback delegates here.
今回は、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.
BeginSpawningActor() must take in a TSubclassOf
parameters named 'Class'. It must also have a out reference parameters of type YourActorClassToSpawn*& named SpawnedActor. This function is allowed to decide whether it wants to spawn the actor or not (useful if wishing to predicate actor spawning on network authority). BeginSpawningActor() can instantiate an actor with SpawnActorDeferred. This is important, otherwise the UCS will run before spawn parameters are set. BeginSpawningActor() should also set the SpawnedActor parameter to the actor it spawned.
[Next, the generated byte code will set the expose on spawn parameters to whatever the user has set]
If you spawned something, FinishSpawningActor() will be called and pass in the same actor that was just spawned. You MUST call ExecuteConstruction + PostActorConstruction on this actor!
This is a lot of steps but in general, AbilityTask_SpawnActor() gives a clear, minimal example.
WaitTargetData
はこの AbilityTasks that want to spawn actors
に該当するため、上記にあるようにまずは AbilityTask_SpawnActor
を確認したいと思います。
SpawnActor
UAbilityTask_SpawnActor | Unreal Engine Documentation
ヘッダファイル
小さいので以下が全体です。
UCLASS() class GAMEPLAYABILITIES_API UAbilityTask_SpawnActor: public UAbilityTask { GENERATED_UCLASS_BODY() UPROPERTY(BlueprintAssignable) FSpawnActorDelegate Success; /** Called when we can't spawn: on clients or potentially on server if they fail to spawn (rare) */ UPROPERTY(BlueprintAssignable) FSpawnActorDelegate DidNotSpawn; /** Spawn new Actor on the network authority (server) */ UFUNCTION(BlueprintCallable, meta=(HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true"), Category="Ability|Tasks") static UAbilityTask_SpawnActor* SpawnActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, TSubclassOf<AActor> Class); UFUNCTION(BlueprintCallable, meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true"), Category = "Abilities") bool BeginSpawningActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, TSubclassOf<AActor> Class, AActor*& SpawnedActor); UFUNCTION(BlueprintCallable, meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true"), Category = "Abilities") void FinishSpawningActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, AActor* SpawnedActor); protected: FGameplayAbilityTargetDataHandle CachedTargetDataHandle; };
ざっと眺めると、BlueprintAssignable
な delegate として Success
と DidNotSpawn
があるのが確認できます。
続いて static
ファクトリも確認できます。
そして、、Activate
が存在しません。最初に貼った AbilityTask.h の説明にあるように Instead of an Activate() function, you should implement a BeginSpawningActor() and FinishSpawningActor() function.
の状態です。
ソースファイル
ファクトリ
UAbilityTask_SpawnActor* UAbilityTask_SpawnActor::SpawnActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, TSubclassOf<AActor> InClass) { UAbilityTask_SpawnActor* MyObj = NewAbilityTask<UAbilityTask_SpawnActor>(OwningAbility); MyObj->CachedTargetDataHandle = MoveTemp(TargetData); return MyObj; }
引数で受け取った TargetData
を Cache している以外は通常の初期化のようです。
BeginSpawningActor
bool UAbilityTask_SpawnActor::BeginSpawningActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, TSubclassOf<AActor> InClass, AActor*& SpawnedActor) { if (Ability && Ability->GetCurrentActorInfo()->IsNetAuthority() && ShouldBroadcastAbilityTaskDelegates()) { UWorld* const World = GEngine->GetWorldFromContextObject(OwningAbility, EGetWorldErrorMode::LogAndReturnNull); if (World) { SpawnedActor = World->SpawnActorDeferred<AActor>(InClass, FTransform::Identity, NULL, NULL, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); } } if (SpawnedActor == nullptr) { if (ShouldBroadcastAbilityTaskDelegates()) { DidNotSpawn.Broadcast(nullptr); } return false; } return true; }
改めてドキュメント貼ります。
BeginSpawningActor() must take in a TSubclassOf
parameters named 'Class'. It must also have a out reference parameters of type YourActorClassToSpawn*& named SpawnedActor. This function is allowed to decide whether it wants to spawn the actor or not (useful if wishing to predicate actor spawning on network authority). BeginSpawningActor() can instantiate an actor with SpawnActorDeferred. This is important, otherwise the UCS will run before spawn parameters are set. BeginSpawningActor() should also set the SpawnedActor parameter to the actor it spawned.
引数として TSubclassOf<AActor> InClass
を引き受けており、AActor*& SpawnedActor
として out reference parameters も持っています。
また SpawnActorDeferred
のインスタンス化も行っています。ここでは spawn された Actor に追加のパラメータの設定はありません。
失敗時には DidNotSpawn
delegate を通知しています。
FinishSpawningActor
void UAbilityTask_SpawnActor::FinishSpawningActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, AActor* SpawnedActor) { if (SpawnedActor) { bool bTransformSet = false; FTransform SpawnTransform; if (FGameplayAbilityTargetData* LocationData = CachedTargetDataHandle.Get(0)) //Hardcode to use data 0. It's OK if data isn't useful/valid. { //Set location. Rotation is unaffected. if (LocationData->HasHitResult()) { SpawnTransform.SetLocation(LocationData->GetHitResult()->Location); bTransformSet = true; } else if (LocationData->HasEndPoint()) { SpawnTransform = LocationData->GetEndPointTransform(); bTransformSet = true; } } if (!bTransformSet) { SpawnTransform = AbilitySystemComponent->GetOwner()->GetTransform(); } SpawnedActor->FinishSpawning(SpawnTransform); if (ShouldBroadcastAbilityTaskDelegates()) { Success.Broadcast(SpawnedActor); } } EndTask(); }
こちらもドキュメントを貼ります。
If you spawned something, FinishSpawningActor() will be called and pass in the same actor that was just spawned. You MUST call ExecuteConstruction + PostActorConstruction on this actor!
Spawn された Actor を受け取って、ExecuteConstruction + PostActorConstruction
を Actor に対して実行しているのが確認できます。ここでは FinishSpawning
で行われています。
AActor::FinishSpawning | Unreal Engine Documentation
ここでは、最後に Success
delegate を通知し、 EndTask
しています。
FGameplayAbilityTargetData
ようやく気づいたのですが、これは前回の WaitOverlap
で取得されたデータと同じ型です。
なので、Overlap
した HitResult に対して Spawn のようなことができるということですね。(あくまで、ここまでの知識の話なので他にもやり方はいくらでもあるのだと思います)
次回
おそらくこの2つで主要なパターンの簡単なものを確認できたので、次回こそは WaitTargetData
を見ていきたいと思います。