Language:
Page Info
Engine Version:

アンリアルでのオブジェクト処理

クラス、プロパティ、関数を適切なマクロでマーク付けすると、それぞれ UClasses、UProperties、UFunctions になります。その結果、アンリアル エンジンがこれらにアクセスできるようになり、 たくさんの内部処理機能を実装ことができます。

ガーベジ コレクション

アンリアル エンジンは、参照されなくなった、または破棄と明示的にフラグ付けされた UObjects を定期的にクリーンアップするガーベジ コレクション スキームを実装しています。アンリアル エンジンでは、 使用中のオブジェクト、あるいはオーファンになっているオブジェクトを判断するための参照グラフをビルドします。このグラフの元は、"root set" と指定されている一連のオブジェクトです。このルートセットには、 どのオブジェクトでも追加できます。ガーベジ コレクションが実施されると、既知の UObject 参照のツリーを"root set" から検索することで、参照されているすべてのオブジェクトの追跡がエンジンで可能になります。参照されていないオブジェクトは、 不要になったとみなされ、取り除かれます。

通常はガーベジ コレクションの対象から外したいオブジェクトに対する UPROPERTY 参照を保持しておく必要があることを覚えておくと便利です。アクタは、通常、アクタが属するレベルなどのルートセットに戻ってリンクしているオブジェクトによって参照されているため、 多くの場合、アクタはこの例外です。アクタは、Destroy() で明示的に破棄とマーク付けすることができます。

参照の自動更新

レベル アンロードによるガーベジ コレクションによって UActorComponent または AActor がクリーンアップされると、オブジェクトへのUPROPERTY 参照は自動的に NULL に更新されます。これは、ダングリング ポインタが残らないようにし、今後トラブルが生じないようにするという点でメリットがあります。 しかし、ポインタがポイントするオブジェクトが明示的に破棄とマーク付けされていれば、UActorComponent ポインタ、または AActor ポインタが予期せず NULL になる場合もあるということです。従って、必ずテストを行ってから 参照を解除するようにしてください。

この機能は、UPROPERTY とマーク付けされている UActorComponent または AActor 参照にのみ適用されることをりっかり理解してください。Raw ポインタに格納されているオブジェクト参照は、アンリアル側では未知のものであり、 自動的に NULL になったり、ガーベジ コレクションを妨げるようなことはありません。UObject* 変数は必ずしも UProperties でなくてもよい、という点に注意してください。UProperty 以外のオブジェクト ポインタが必要な場合は、TWeakObjectPtr<> の使用を検討してください。 このポインタは弱いのでガーベジ コレクションを妨げませんが、アクセス前に有効性のクエリが可能です。

シリアル化

UObject がシリアル化されると、"transient" と明示的にマーク付けされていない限り、すべての UProperty 値が自動的に書き込みまたは読み出されます。例えば、AEnemy インスタンスをレベルに配置し、 そのヘルスを 500 に設定して保存すると、UClass 定義以外に一行もコードを記述することなく、リロードできます。

UProperties が追加または取り除かれると、事前に存在していたコンテンツのロードがシームレスに処理されます。新規プロパティは、新規 CDO からコピーされたデフォルト値を取得します。取り除かれたプロパティは、警告なしに無視されます。

カスタム仕様のビヘイビアが必要ならば、カスタムのシリアル化を書き出せば適宜コードのバージョン体系化が可能ですが、実際にはあまり使われません。

プロパティ値を更新する

UClass の CDO を変更すると、アンリアル エンジンではこうした変更をロード時にクラスのすべてのインスタンスにインテリジェントに適用しようとします。任意のオブジェクト インスタンスについて、 更新された変数が、その前のデフォルトから変わっていなければ、新しいデフォルト オブジェクトに更新されます。変数に何らかの変更があった場合、その値は意図的に設定されたものであるとみなされ、 そうした変更は元に戻されません。

例えば、いくつかの AEnemy オブジェクトを配置したレベルを保存し、ヘルスのデフォルト値を AEnemy コンストラクタで 100 に設定したとします。さらに、Enemy_3 は特にタフであるため、 ヘルスを 500 に設定したとします。

その後、気が変わって、ヘルスのデフォルト値を 150 に増やしたと想定します。次回、レベルをロードすると、アンリアル エンジンは、CDO が変更されたことを認識し、Enemy_3 以外は 150 のヘルス値を持つように、 AEnemy のすべてのインスタンスを更新します。Enemy_3 のヘルスは 500 のままです。

プロパティの自動初期化

コンストラクタが呼び出される前に、UObjects は初期化時に自動的にゼロになります。これはクラス全体、UProperties、ネイティブ メンバでも同様に起こります。続いて、クラス コンストラクタのカスタム値で メンバが初期化されます。

エディタの統合

UObjects と UProperties はエディタによって認識されます。エディタはこうした値を編集のために自動的に公開します。特殊なコードを記述する必要はありません。 これにはオプションでブループリント ビジュアル スクリプティング システムへの統合が含まれます。変数と関数のアクセシビリティと公開を制御するためのオプションが数多くあります。

ランタイム時の型情報

UObjects はどの UClass であるかを常に認識しており、ランタイム時に型関連の決定が行われます。

ネイティブ コードでは、すべての UObject クラスは、その親クラスに設定されたカスタムの"Super" typedef を持ちます。これにより、挙動のオーバーライドを簡単に制御することができます。例えば、以下のようになります。

class AEnemy : public ACharacter
{
    virtual void Speak()
    {
        Say("Time to fight!");
    }
};

class AMegaBoss : public AEnemy
{
    virtual void Speak()
    {
        Say("Powering up! ");
        Super::Speak();
    }
};

ご覧になってわかるように、 Speak() を呼び出すと、 MegaBoss が"Powering up! Time to fight!" といいます。

さらに、テンプレート化された Cast 関数を使用して、基底クラスからのオブジェクトを派生クラスに安全にキャストできます。または、 IsA() を使用してオブジェクトが特定のクラスであるかをクエリすることができます。以下に簡単な例を示します。

class ALegendaryWeapon : public AWeapon
{
    void SlayMegaBoss()
    {
        TArray<AEnemy> EnemyList = GetEnemyListFromSomewhere();

        // The legendary weapon is only effective against the MegaBoss
        for (AEnemy Enemy :EnemyList)
        {
            AMegaBoss* MegaBoss = Cast<AMegaBoss>(Enemy);
            if (MegaBoss)
            {
                Incinerate(MegaBoss);
            }
        }
    }
};

ここでは、Cast<> を使用して AEnemy を AMegaBoss にキャストしようとしています。問題となっているオブジェクトが実際には AMegaBoss (またはその子クラス) ではない場合、 このキャストは NULL を戻し、適切に対応することができます。この場合、この伝説の武器は効果がありません。

ネットワークのレプリケーション

UObject システムには、ネットワーク通信とマルチプレイヤー ゲームを行いやすくする堅牢な機能セットがあります。

UProperties をタグ付けして、アンリアル エンジンに対してネットワーク プレイ中にそのデータをレプリケートするように指示できます。この場合の一般的なモデルとしては、変数がサーバー上で変更され、 続いてアンリアル エンジンがこの変更を検知し、すべてのクライアントに変更を確実に送信します。レプリケーションによって変数が変更する場合に、クライアントはオプションでコールバック関数を受信します。

UFunctions はリモート マシン上で実行するようにタグ付けすることも可能です。例えば、「サーバー」の関数は、クライアントのマシン上で呼び出されると、 そのアクタのサーバーのバージョンに対して関数を実際にサーバーマシン上で実行させます。一方 "client" 関数はサーバーから呼び出し可能で、そのアクタの所有クライアント版で実行されます。