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

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

UE4/C++: NewObject で生成したオブジェクトの Outer を変更したい場合のメモ

↓こういうのが居たとする。

// UObject 派生だがシングルトンファクトリーで生成されるオブジェクトがあったとする
UA* UA::GetInstance()
{
  if ( ! my_static_instance )
    my_static_instance = NewObject< UA >();
  return my_static_instance;
}

このようなファクトリーで得られる仕組みの UA のオブジェクトは Outer を持たないため GetWorld() しても nullptr が帰るなど不便な事もある。幾つか無理やり気味な方法は思いつくものの、 Outer を外から設定できてしまえば何かと解決する。この悩み事が生じた際は UObject::Rename が最も簡単な解決方法になる事がある。

// UGameInstance など World に絆付けされた何かから
void UMyGameInstance::Something()
{
  auto a = UA::GetInstance();
  a.Rename( nullptr, this );
}

こうすると、以降は UA のオブジェクト内部やそこからさらに NewObject<T>( this ) で生成したオブジェクトは Outer の絆により GetWorld() も期待動作するようになる。もちろん、もっと簡単な用途で単純に Outer を変更したい場合も UObject::Rename で同様に Outer を変更できる。

UObject::Rename は関数名からはオブジェクトの名前を変更するだけに見えるが、シグニチャーを見れば第2引数に UObject * NewOuter とある。実装では第1引数に NULL がデフォルトで与えられているが省略して呼ぶ意味があるかはさておき、名前を変更する必要の無い場合には nullptr を与えれば良い事を推量できる。(実際に第1引数は nullptr でそのように動作させられる)

// Runtime/CoreUObject/Public/UObject/Object.h
    /** Rename this object to a unique name.*/
    virtual bool Rename( const TCHAR* NewName=NULL, UObject* NewOuter=NULL, ERenameFlags Flags=REN_None );

例によって事実上アンドキュメントなので使う場合はソースを読んだ方が良い。

// Runtime/CoreUObject/Private/UObject/Obj.cpp
bool UObject::Rename( const TCHAR* InName, UObject* NewOuter, ERenameFlags Flags )

ERenameFlagsRF_ClassDefaultObject, REN_ForceGlobalUnique, REN_Test, REN_ForceNoResetLoaders, REN_NonTransactional, REN_DoNotDirty, RF_Public など指定すると意味があるようだが、さしあたり Outer を設定、あるいは挿げ替えたいだけの場合には実引数は省略して REN_None を与えれば良い。実装詳細的には LowLevelRenamePostRename も呼んでいるので思いのほか複雑な事をしている。

References