UE4: Blueprint 向けに WorldContextObject が必要な機能を提供する際に暗黙的に WorldContextObject を扱わせられるように C++ コードで対応する方法
概要
状況の例として、 Blueprint へ特定のアクターを引っ張り出させる次のような実装を提供したいとする。
設計上必ず1つ AMySpecialActor が配置されていて、
// 定義はどうあれ、どこかに定義され1つだけは常に配置される AMySpecialSomething アクター UCLASS() class AMySpecialSomething: public AActor { GENERATED_BODY() };
それを Blueprint からさくっと引っ張れる機能提供を C++ コードで実装したい、と:
動作しない、少し惜しい実装
UCLASS() class UMySpecialUtility : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION( BlueprintCallable, Category="My Special Utility", BlueprintPure ) static AMySpecialSomething* GetSpecialSomething() { // note: ここで nullptr が取得され、この実装は意図した動作をしない。 const auto world = GEngine->GetWorld(); if ( ! IsValid( world ) ) return nullptr; TArray< AActor* > results; UGameplayStatics::GetAllActorsOfClass( world, AMySpecialSomething::StaticClass(), results ); if ( ! results.IsValidIndex( 0 ) ) return nullptr; return Cast< AMySpecialSomething >( results.GetData()[ 0 ] ); } };
コメントに書いたように、適当なアクターのメンバー関数の中で実装する場合と違い、 UBlueprintFunctionLibrary に static メンバー関数として実装した場合、この実装では期待動作しない。
動作する、 Blueprint からも扱いやすい実装
UFUNCTION( BlueprintCallable, Category="My Special Utility", BlueprintPure, meta = ( WorldContext = WorldContextObject) ) static AMySpecialSomething* GetSpecialSomething( const UObject* WorldContextObject ) { const auto world = GEngine->GetWorldFromContextObject( world_context_object, EGetWorldErrorMode::LogAndReturnNull ); if ( ! IsValid( world ) ) return nullptr; TArray< AActor* > results; UGameplayStatics::GetAllActorsOfClass( world, AMySpecialSomething::StaticClass(), results ); if ( ! results.IsValidIndex( 0 ) ) return nullptr; return Cast< AMySpecialSomething >( results.GetData()[ 0 ] ); } };
外部から WorldContextObject
の供給を受ければ問題無く動作する。ついで、その際に Blueprint 向けの UFUNCTION
の定義に WorldContext
を与えておくと、 Blueprint でこの関数を使用する際に、暗黙的に WorldContextObject
が適切に与えられ、かつ Blueprint では余計なコネクターなども綺麗に省略される状態にできる。
めでたしめでたし。