クラス、プロパティ、関数を適切なマクロでマーク付けすると、それぞれ UClasses、UProperties、UFunctions になります。その結果、Unreal Engine がこれらにアクセスできるようになり、たくさんの内部処理機能を実装することができます。
プロパティの自動初期化
コンストラクタが呼び出される前に、UObjects は初期化時に自動的にゼロになります。これはクラス全体、UProperties、ネイティブ メンバーでも同様に起こります。続いて、クラス コンストラクタのカスタム値でメンバーが初期化されます。
参照の自動更新
AActor または UActorComponent が破棄されるか、プレイから取り除かれると、それに対するすべての参照でリフレクション システムで可視のものは (UProperty ポインタおよび TArray など Unreal Engine コンテナ クラスに保存されているポインタ) 自動的に null になります。これは、ダングリング ポインタが残らないようにし、今後トラブルが生じないようにするという点でメリットがありますが、コードの他の部分がそれらを破棄すると、AActor ポインタと UActorComponent ポインタが null になるということも意味します。一番のメリットは、null チェックの方が信頼性が高いということです。標準ケースの null ポインタと non-null ポインタが破棄されたメモリをポイントしている場合の両方を検出するからです。
この機能は、UPROPERTY とマーク付けされているか、Unreal Engine のコンテナ クラスに格納されている UActorComponent または AActor 参照にのみ適用されることを理解することが重要です。Raw ポインタに格納されているオブジェクト参照は、Unreal Engine 側では未知のものであり、自動的に null にはならず、ガーベジ コレクションも妨げません。UObject* 変数は必ずしも UProperties でなくてもよい、という点に注意してください。UProperty 以外のオブジェクト ポインタが必要な場合は、TWeakObjectPtr の使用を検討してください。これは、弱いポインタであるため、ガーベジ コレクションを防ぎませんが、アクセスされる前に、妥当性についてクエリ可能であり、ポイントしているオブジェクトが破棄されると null になるように設定されます。
参照された UObject UProperty が自動的に null になる別のケースは、エディタでアセットに対して 'Force Delete' を使用する場合です。結果として、アセットである UObject を操作するすべてのコードは、こうしたポインタが null になるように処理しなければなりません。
シリアル化
UObject がシリアル化されると、「transient」と明示的にマーク付けされていない限り、すべての UProperty 値が自動的に書き込みまたは読み出されます。または、ポスト コンストラクタのデフォルト値から変更されません。たとえば、AEnemy インスタンスをレベルに配置し、ヘルスを 500 に設定して保存して UClass 定義以外に 1 行もコードを記述することなく、リロードすることができます。
UProperty が追加または取り除かれると、事前に存在していたコンテンツのロードがシームレスに処理されます。新規プロパティは、新規 CDO からコピーされたデフォルト値を取得します。取り除かれたプロパティは、警告なしに無視されます。
カスタムのビヘイビアが必要な場合、UObject::Serialize 関数をオーバーライドすることができます。これはデータ エラーの検出、バージョン番号のチェック、データ フォーマットが変わったときに自動変換を実行または更新する場合に便利です。
プロパティ値を更新する
UClass の Class Default Object (CDO) を変更すると、エンジンではこうした変更をロード時にクラスのすべてのインスタンスに適用しようとします。任意のオブジェクト インスタンスに対して、更新された変数の値が古い CDO の値と一致する場合、新しい CDO で保持されている値に更新されます。変数に他の値がある場合、その値は意図的に設定されたものであるとみなされ、こうした変更は保たれます。
たとえば、いくつかの AEnemy オブジェクトを配置したレベルを保存し、ヘルスのデフォルト値を AEnemy コンストラクタで 100 に設定したとします。非常にタフであるため、Enemy_3 のヘルスを 500 に設定したとします。その後、気が変わって、ヘルスのデフォルト値を 150 に増やしたと想定します。次回、レベルをロードすると、Unreal Engine は、CDO が変更されたことを認識し、古いヘルスのデフォルト値 (100) を持つ AEnemy のすべてのインスタンスを 150 のヘルス値を持つように更新します。Enemy_3 のヘルスは 500 のままです。古いデフォルト値を使用していなかったためです。
エディタの統合
UObjects と UProperties はエディタによって認識されます。特殊なコードを記述する必要なくエディタはこうした値を編集のために自動的に公開します。これにはオプションでブループリント ビジュアル スクリプティング システムへの統合が含まれます。変数と関数のアクセシビリティと公開を制御するためのオプションが数多くあります。
ランタイムの型情報とキャスティング
UObjects は、Unreal Engine のリフレクション システムの一部であり、どの 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();
// 伝説の武器は、MegaBoss にしか効果がない
for (AEnemy Enemy : EnemyList)
{
AMegaBoss* MegaBoss = Cast<AMegaBoss>(Enemy);
if (MegaBoss)
{
Incinerate(MegaBoss);
}
}
}
};
ここでは、Cast を使用して AEnemy を AMegaBoss にキャストしようとしています。問題となっているオブジェクトが実際には AMegaBoss (またはその子クラス) ではない場合、キャストは nulll ポインタを返し、適切に反応することができます。上のコードでは、Incinerate 関数は MegaBoss に対してのみ呼び出されます。
ガベージ コレクション
Unreal は、参照されなくなった、または破棄と明示的にフラグ付けされた UObjects を定期的にクリーンアップするガーベジ コレクション スキームを実装しています。どの UObjects がまだ使用中であるか、どのオブジェクトが孤立しているかを判断するための参照グラフをビルドします。このグラフの元は、「root set」と指定されている一連の UObjects です。どの UObject でもルートセットに追加することができます。ガーベジ コレクションが実施されると、既知の UObject 参照のツリーを「root set」から開始して検索することで、参照されているすべての UObjects を追跡することができます。参照されていない UObjects、つまりツリー検索で見つからなかったものは、不要とみなされ、取り除かれます。
単純なオブジェクト ポインタか、TArray<UObject*> などオブジェクト ポインタ型を格納する Unreal Engine のコンテナ クラスかに関係なく、通常はガーベジ コレクションの対象から外したいオブジェクトに対する UPROPERTY 参照を保持しておく必要があります。多くの場合、アクタはとそのコンポーネントは、この例外です。アクタは通常、ルートセットにリンクバックするオブジェクトによって参照されるからです。たとえば、それらが属するレベルなどで、アクタのコンポーネントはアクタ自体によって参照されます。アクタは、Destroy 関数を呼び出すことで明示的に破棄とマーク付けされます。これは進行中のゲームからアクタを取り除く標準的な方法です。コンポーネントは、DestroyComponent 関数を使って明示的に破棄できますが、通常は所有しているアクタがゲームから取り除かれると破棄されます。
Unreal Engine 4 のガーベジ コレクションは高速かつ効率的です。孤立したオブジェクトを特定するためのマルチスレッドの到達可能性分析や、コンテナからアクタをできるだけ早く取り除くために最適化されたコードの unhash など、オーバーヘッドを最小限に抑えるように設計された数多くのビルトイン機能があります。いつどのようにガーベジ コレクションを行うかを一段と正確に調整する機能もあります。そのほとんどは、[Project Settings (プロジェクト設定)] の [Engine - Garbage Collection (エンジン - ガベージ コレクション)] にあります。以下の設定はプロジェクトのガーベジ コレクターのパフォーマンスを調整するために一般的に使用されるものです。
| 設定項目 | 機能説明 |
|---|---|
| Create Garbage Collector UObject Clusters (ガーベージ コレクター UObject クラスタの作成) | プロジェクトの設定でオン / オフ (デフォルトはオン) を切り替えることができます。オンの場合、関連オブジェクトはガーベジ コレクション クラスタでグループ化されて、個々のオブジェクトをチェックするのではなく、クラスタだけをチェックすればよいようにします。つまり、到達可能性は速くなります。クラスタ全体がひとつのオブジェクトとして扱われるためです。しかし、クラスタ内の個々のアイテムはすべて unhash されて、同じフレームで削除するように準備されます。クラスタが十分に大きいと処理落ちを生じる可能性があります。一般的にクラスタを作成することでガーベジ コレクションのパフォーマンスが向上し、到達可能性分析にかかる時間が短縮します。 |
| Merge GC Clusters (GC クラスタをマージ) | クラスタのマージを有効にして、あるクラスタからのオブジェクトが別のクラスタのオブジェクトを参照する場合にクラスタをマージさせることができます。マージを生じさせた参照をクリアしても、新しくマージされたクラスタが解消されたり、バラバラになることはありません。この機能が動作するためには、[Create Garbage Collector UObject Clusters (ガーベージ コレクター UObject クラスタの作成] を有効にしなければなりません。これはガーベジ コレクターがオブジェクトを unhash し、破棄する頻度を減らすことになりますが、オブジェクト数が多くなると一度に unhash、および破棄します。さらに、マージしたクラスタなしでガーベジ コレクションが起こると、マージしたクラスタでガーベジ コレクションが行われない場合もあります。クラスタ内の任意のオブジェクトに対する参照では、クラスタ全体がガーベジ コレクションの対象になるからです。 |
| Actor Clustering Enabled (アクタ クラスタリングが有効) | [Project Settings (プロジェクト設定)] でこのオプションをオンにして、bCanBeInCluster 変数を true に設定するか、コードの CanBeInCluster 関数をオーバーライドして true を戻すようにすることでアクタをクラスタに入れることができます。デフォルトでアクタとコンポーネントでは、これをオフにしています。ただし、スタティックメッシュ アクタと Reflection Capture コンポーネントは例外です。この機能は、一度に破棄することを想定しているアクタをグループ化するのに便利です。通常はそれらを含むサブレベルをアンロードすること以外では破棄できないレベルに配置しているスタティックメッシュになります。 |
| Blueprint Clustering Enabled (ブループリント クラスタリングが有効) | ブループリントの UBlueprintGeneratedClass と共有の UPROPERTY や UFUNCTION などの関連データは、この設定をオンにすることでクラスタ化することができます。このクラスタリングは、ブループリントで生成されたクラス自体を参照し、ブループリントの個々のインスタンスは参照しないことを覚えておいてください。 |
| Time Between Purging Pending Kill Objects (保留中のキル オブジェクトをパージする間の時間) | ガーベジ コレクションのアクティビティの頻度はプロジェクト設定で調整することができます。こうしたハイレベルな制御は、処理落ちを防ぐうえで特に役立ちます。コレクション間の時間を短縮することで、次回の到達可能性分析のパスで見つかる可能性のある到達不可能になりそうなオブジェクト量を減らして、大量のアクタを同時にクリーンアップする場合に起こる処理落ちを避けることができます。 |
[Project Settings] 内のガーベジ コレクション設定
ネットワークのレプリケーション
UObject システムには、ネットワーク通信とマルチプレイヤー ゲーム を行いやすくする堅牢な機能セットがあります。
UProperties にタグ付けして、エンジンに対して ネットワーク プレイ中にデータをレプリケート するように指示できます。この場合の一般的なモデルでは、変数がサーバー上で変更されると、エンジンがその変更を検出し、すべてのクライアントに確実に送信します。レプリケーションによって変数が変更する場合に、クライアントはオプションでコールバック関数を受信します。
UFunctions は リモート マシン上で実行する ようにタグ付けすることも可能です。たとえば、「サーバー」の関数は、クライアントのマシン上で呼び出されると、その関数がアクタのサーバー版でサーバー マシンで実行するようにします。一方、「クライアント」の関数はサーバーから呼び出し可能で、そのアクタの所有クライアント版で実行されます。