AYU MAX

プログラミングとか作ったもの

UProperty -> FPropertyへの変化(UE4 4.25)

Unreal Engine 4.25 Preview

先日UE4の4.25のPreviewがダウンロードできるようになりました。

(この記事を書いている段階ではPreview3になっています)

その内容は以下のページに記載されています。

forums.unrealengine.com

たくさんある更新内容の中で、以下の項目が目に留まりました。 (FProperty Refactor...の部分です)

f:id:ayuma0913:20200322125250p:plain

内容は「4.25からはUPropertyはFPropertyとなってUObjectではなくなりました」というものです。

今までUPorpertyはUObjectを継承していました。 UE4ではUObjectを継承しているクラスはUから始まる命名規則があります。そのためUPropertyというクラス名でした。

それがUObjectを継承しなくなった(FFieldというクラスを継承するようになっています)ため、FPropertyというクラス名に変わったということでした。

UPropertyとは

そもそも今回変更されたUProperty(FProperty)とは何なのかというと、UE4のプロパティシステムによって作られたC++クラスのメンバー変数または関数の引数の情報になります。

詳細は以下の公式ページに詳しく説明されています。

更にUPropertyはプロパティの型によって、UStringPropertyやUBoolPropertyなどに派生されています。

UE4のエンジン内部では色々な用途に使われていますが、我々UE4を使う側の立場では、例えば以下のようなことをしたい時に使うことになります。

  • ブループリントで定義したオブジェクトのプロパティにC++側からアクセスしたい
  • オブジェクトの独自のシリアライザを作りたい
  • オブジェクトのプロパティをプロパティ名を指定して触りたい

このようにUPropertyを使ってUE4のプロパティシステムを使うと、より柔軟にオブジェクトを操作する仕組みが構築できます。

(参考)オブジェクトの全てのプロパティの値を取得する例

以下のコードはObjの全てのプロパティの値を取得しています。

(※ この例では全てのプロパティの型はuint8であるという前提です)

for (TFieldIterator<UProperty> PropIt(Obj->GetClass()); PropIt; ++PropIt)
{
    UProperty* Property = *PropIt;

    // プロパティ名を取得
    FString PropertyName = Property->GetName();

    // プロパティの値を取得
    uint8 CurrentPropAddr = *(PropIt->ContainerPtrToValuePtr<uint8>(Obj));
}

なぜFPropertyに変更されたのか

全部の情報を追い切れてないので個人的な想像を多分に含みますが、UObjectで無くすことによりGC(ガベージコレクション)の管理対象外にしたかったのが大きな理由なのかなと考えています。

UObjectとしてインスタンスをメモリに確保すると、GCが適切なタイミングでインスタンスを破棄するべきかどうかをチェックします。 チェックの結果破棄するべきとなったインスタンスは破棄されることになります。

プロジェクト全体でUObjectのインスタンス数が多いと、このGCが破棄対象をチェックする際の検索時間が多くなってしまいます。 (調べる対象の個数が絶対的に多くなってしまうため)

検索時間が多くなると、ひどくなるとGCのチェックタイミングでフレームレートがガクンと落ちたりするほど悪影響がでます(経験あり)

そのためケースバイケースではありますが、UObjectのインスタンス数はなるべく少ないほうがパフォーマンス上は良くなると考えられます。

従来(4.24まで)のC++コードを4.25でビルドすると

試しに先ほど書いたオブジェクトのプロパティを列挙するコードを4.25でビルドしてみました。

するとエラーにはなりませんでしたが、以下のWarningがでています。

warning C4996: UProperty has been renamed to FProperty Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.

「UPropertyはFPropertyに変わったから、早く変えてね」っというWarningです。ですがWarningということはこのままでも一応動くということになります。

その理由は4.25でのUPropertyの定義を見ると分かります。

DefineUPropertyMacros.hというヘッダに以下のように定義されていました。

f:id:ayuma0913:20200322135332p:plain

U~Propertyは全てF~Propertyとして置換されるようになっています。

この置換する際にDEPRECATED_MACROも埋め込まれているため、先ほどのWarningがでているようです。

では、とりあえずはUPropertyを使っているコードでも当面は何もしなくて良いのかというとそうではないようです。

置換されているだけでC++コードに書かれているUPropertyは既にUObjectではないので、以下のようなコードはエラーとなります。

UProperty* Property = *PropIt;

if (UBoolProperty* BoolProperty = Cast<UBoolProperty>(Property))
{
    bool bValue = BoolProperty->GetPropertyValue(Obj);
}

上記は「あるプロパティの型がbool型だったらboolとして値を取得する」といった意味のコードです。

この中でCast<UBoolProperty>(Property)を使ってUPropertyをUBoolPropertyにキャスト可能か調べているのですが、CastはUObjectに対してしか使用することはできません。

そのためこのコードをビルドするとエラーとなります。

(※ 上記の例ではCastField関数に置き換える事でFPropertyでも同じことができます)

同様の理由でIsValidなんかもダメになりそうです。

その他UPropertyをUObjectとして引数に関数に渡している部分がある場合は、適切に変更する必要があります。

まとめ

  • 4.25からはUPropertyはFPropertyになりました
  • C++でU~Propertyを使っている箇所はF~Propertyに置換しましょう
  • ただ置換するだけでなくUPropertyをUObjectとして使っている箇所がないかチェックしましょう

おまけ

以前のままのUProperty定義はUnrealTypePrivate.h内に残っているため、このヘッダをIncludeするともうしばらくUPropertyは延命できそうな気もしますが、あるべき流れからは外れる事になりそうなので使わないほうが良さそうかな。