C++ ときどき ごはん、わりとてぃーぶれいく☆

Wonder Rabbit Projectのなかのひとのブログ。主にC++。

UE4: Blueprint 向けに WorldContextObject が必要な機能を提供する際に暗黙的に WorldContextObject を扱わせられるように C++ コードで対応する方法

概要

状況の例として、 Blueprint へ特定のアクターを引っ張り出させる次のような実装を提供したいとする。

設計上必ず1つ AMySpecialActor が配置されていて、

// 定義はどうあれ、どこかに定義され1つだけは常に配置される AMySpecialSomething アクター
UCLASS() class AMySpecialSomething: public AActor { GENERATED_BODY() };

f:id:USAGI-WRP:20180309095116p:plain

それを 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 では余計なコネクターなども綺麗に省略される状態にできる。

f:id:USAGI-WRP:20180309102714p:plain

めでたしめでたし。

参考

  1. Metadata Specifiers | Unreal Engine
  2. GEngine - GetWorld() return NULL ?? - UE4 AnswerHub