Language:
Page Info
Engine Version:
The translation of this page is out of date. Please see the English version for the latest version of the page.

ゲームプレイ クラス

アンリアルエンジンで使用するすべてのゲームプレイ クラスは、クラス ヘッダ ファイル (.h) とクラス ソース ファイル(.cpp)で構成 されています。クラス ソース ファイルは、クラスに属する関数を 実装 することによってクラスの機能を定義するファイルである一方で、 クラス ヘッダ ファイルにはクラスの宣言と変数や関数など、そのメンバーが 含まれています。

アンリアルエンジンのクラスには標準化した命名スキームがあり、クラスの最初の文字 (プレフィックス) を見ただけで、 瞬時にクラスの種類が判断できます。以下はゲームプレイ クラスのプレフィックスです。

プレフィックス

意味

A

スポーン可能 なゲームプレイ オブジェクトの基底クラスからの拡張です。これらはアクタで、ワールド内へ直接スポーンすることができます。

U

全ゲームプレイ オブジェクトの基底クラスからの拡張です。このクラスはワールド内へ直接インスタンス化することはできません。アクタに属さなければいけません。通常は コンポーネント のようなオブジェクトです。

クラスの追加

C++ クラス ウィザード は、新規クラスに必要なヘッダ ファイルとソース ファイルを設定し、それに応じてゲーム モジュールを更新します。 ヘッダ ファイルとソース ファイルは、 UCLASS() マクロのようなアンリアル エンジン固有のコードに加えて、クラス宣言とクラス コンストラクタを自動的にインクルードします。

クラス ヘッダ

アンリアル エンジンのゲームプレイクラスにはそれぞれ、固有のクラス ヘッダ ファイルがあります。ファイルには、その中で定義されているクラスと一致する名前を付けなければなりません。 A または U のプレフィックスを差し引いて「.h 」ファイル拡張子を使用します。例えば AActor クラスのクラス ヘッダ ファイルの名前は Actor.h となります。 ゲーム プレイ クラスのクラス ヘッダ ファイルは、

クラス、変数、関数の宣言プロセスを単純化するために、指定されたマクロと標準の C++ 記法を併用します。

各ゲームプレイ クラスのヘッダ ファイル最上部には、以下のようにクラス用に (自動) 生成されたヘッダ ファイルをインクルードしなくてはいけません。従って ClassName.h の上には次の行が表示されます。

#include "ClassName.generated.h"

クラスの宣言

クラスの宣言はクラス名と継承されたクラスを定義します。 つまり、継承する関数とクラスや、クラス指定子 やメタデータ経由で望まれるエンジンやエディタのその他の特定のビヘイビアです。クラスの宣言の記法は以下の通りです。

UCLASS([specifier, specifier, ...], [meta(key=value, key=value, ...)])
class ClassName : public ParentName
{
    GENERATED_BODY()
}

宣言は、クラス用の標準の C++ クラス宣言で構成されています。標準の宣言の上で、クラス指定子やメタデータなどの記述子が UCLASS マクロへ 渡されます。宣言されるクラス用に UClass を作成するために使用され、エンジンの指定されたクラス表現とみなされます。また、GENERATED_BODY() マクロを クラス本体の冒頭に配置しなくてはいけません。

クラス指定子

クラスを宣言するときに、エンジンやエディタの様々な側面でクラスがどのように動作するかを制御する指定子を 宣言に追加することができます。

メタデータ指定子

メタデータ指定子の使用方法は一般的なクラス、関数、インターフェースでは異なります。

クラスは以下のメタタグ指定子を使用することができます。

クラスの実装

すべてのゲームプレイ クラスは、適切に実装するために GENERATED_BODY マクロを使用しなければなりません。これは、クラスとそのすべての変数と関数を定義するクラスヘッダ (.h) ファイルで 行われます。クラス ソース ファイルとヘッダー ファイルのベスト プラクティスは、AU のプレフィックスを取り除いて、実行中のクラスに一致する名前をつけることです。従って、AActor クラスのソース ファイルは Actor.cpp という名前になります。そのヘッダ ファイルは Actor.h という名前になります。これは、エディタ内の "Add C++ Class" メニュー オプションによって作成されるクラスを自動的に処理します。

このソース ファイル (.cpp) には C++ クラス宣言を含むヘッダ ファイル (.h) がインクルードされていなければなりません。これは通常自動生成されますが、必要に応じて手書きのコードでも作成可能です。例えば、AActor クラスに対する C++ 宣言は EngineClasses.h ヘッダ ファイルで自動生成されます。つまり Actor.cpp ファイルには、EngineClasses.h ファイルかこのファイルをインクルードする 別のファイルがインクルードされていなければなりません。一般的には、ゲーム プロジェクト用のヘッダ ファイルをインクルードし、それがゲーム プロジェクトのゲームプレイ クラスのヘッダをインクルードします。AActorEngineClasses.h の場合、 Engine プロジェクト用のヘッダ ファイルである Engine.h をインクルードする EnginePrivate.h ヘッダがインクルードされ、それが Engine.hヘッダ ファイルをインクルードします。

#include "EnginePrivate.h"

インクルードされていないクラスの関数の実装で他のクラスを参照する場合、ファイルを追加でインクルードする必要があるかもしれません。単にそのファイルをインクルードして追加します。

クラス コンストラクタ

UObjectsコンストラクタ を使ってプロパティおよびその他の変数のデフォルト値の設定や、その他必要な初期化を実行します。クラス コンストラクタは通常、クラス実装ファイル内に置かれます。 例えば、AActor::AActor コンストラクタであれば Actor.cpp の中です。

コンストラクタは、モジュール別に特別な「constructors」ファイルに置かれる場合もあります。例えば、AActor::AActor コンストラクタは EngineConstructors.cpp 内に存在します。これは 以前に、コンストラクタの採用に DEFAULTS ブロックを使用した自動変換プロセスの結果です。これらは時間と共にそれぞれのクラス ソース ファイルに移動していきます。

コンストラクタ インラインをクラス ヘッダ ファイルに配置することも可能です。ただし、コンストラクタがクラス ヘッダ ファイル内にある場合、 自動コードジェネレータがヘッダ中でコンストラクタ宣言を作成するのを阻まないように、 UClass は CustomConstructor 指定子で宣言されなければなりません。

コンストラクタの形式

以下が、 UObject コンストラクタの最も基本的な形式です。

UMyObject::UMyObject()
{
    // Initialize CDO properties here. (ここで CDO プロパティを初期化します。)
}

このコンストラクタが Class Default Object (CDO) を初期化し、クラスのその後のインスタンスがベースとするマスター コピーになります。特別なプロパティ変更構造体をサポートする セカンダリ コンストラクタもあります。

UMyObject::UMyObject(const FObjectInitializer& ObjectInitializer)
:Super(ObjectInitializer)
{
    // Initialize CDO properties here. (ここで CDO プロパティを初期化します。)
}

上記のコンストラクタは両方とも実際には初期化は実行しませんが、エンジンがすべてのフィールドをゼロ、NULL、またはデフォルト コンストラクタが実装する値にしているはずです。 ただし、コンストラクタに書き込まれた初期化コードは CDO に適用されるので、 CreateNewObjectSpawnActor など、エンジン内で正しく作成されたオブジェクトの新規インスタンスにコピーされます。

コンストラクタにパスされる FObjectInitializer パラメータは、const とマークされますが、プロパティとサブオブジェクトをオーバーライドするための設定が可能です。作成された UObject にこれらの値が反映されるので、 それを使って登録されたプロパティまたはコンポーネントの値を変更することができます。

AUDKEmitterPool::AUDKEmitterPool(const FObjectInitializer& ObjectInitializer)
:Super(ObjectInitializer.DoNotCreateDefaultSubobject(TEXT("SomeComponent")).DoNotCreateDefaultSubobject(TEXT("SomeOtherComponent")))
{
    // Initialize CDO properties here. (ここで CDO プロパティを初期化します。)
}

上記のケースでは、おそらく、スーパークラスは "SomeComponent" および "SomeOtherComponent" という名前のサブオブジェクトをコンストラクタ内に作成しようとしますが、FObjectInitializer があるので作成はしません。 次のケースでは、SomeProperty が CDO のデフォルトを 26 にするので、AUTDemoHUD の各インスタンスが新しくなります。

AUTDemoHUD::AUTDemoHUD()
{
    // Initialize CDO properties here. (ここで CDO プロパティを初期化します。)
    SomeProperty = 26;
}

Constructor Statics と Helpers

さらに複雑なデータ型、特にクラスの参照、名前、アセットの参照のために値を設定する場合、 必要な様々なプロパティ値を保持するためにコンストラクタ内で ConstructorStatics 構造体を定義およびインスタンス化することが求められます。ConstructorStatics 構造体は、コンストラクタの初回実行時のみ作成されます。次の実行時にはポインタをコピーするだけなので、 処理速度が超高速になります。ConstructorStatics 構造体が作成されると、後にコンストラクタで実際のプロパティに値を代入する時に、 アクセスできるように構造体のメンバに値が割り当てられます。

ContructorHelpers は、コンストラクタ特有の共通のアクションを実行するために使用するヘルパー テンプレートを含む ObjectBase.h で定義される特別な名前空間です。例えばヘルパー テンプレートには、 アセットやクラスへの参照を検索するためだけでなく、コンポーネントを作成したり検索するものもあります。

アセットの参照

クラスにアセットの参照が存在しないことが理想的です。ハードコード化されたアセットの参照は壊れやすいので、アセット プロパティの設定にはブループリントの使用が推奨されてきました。しかしながら、 ハードコード化された参照は完全にサポートされています。オブジェクトを構築するたびにアセットの検索を行いたくないため、検索は 1 度だけ行います。静的な構造体を使用して、アセット検索は必ず 1 回だけ 実行するようにできます。

ConstructorHelpers::FObjectFinderStaticLoadObject を使って指定された UObject への参照を検索します。一般的にこれはコンテンツ パッケージに格納されているアセットの参照に使用します。オブジェクトが見つからない場合、 失敗したことが報告されます。

ATimelineTestActor::ATimelineTestActor()
{
    // Structure to hold one-time initialization
    struct FConstructorStatics
    {
        ConstructorHelpers::FObjectFinder<UStaticMesh> Object0;
        FConstructorStatics()
        :Object0(TEXT("StaticMesh'/Game/UT3/Pickups/Pickups/Health_Large/Mesh/S_Pickups_Base_Health_Large.S_Pickups_Base_Health_Large'"))
        {
        }
    };
    static FConstructorStatics ConstructorStatics;

    // Property initialization (プロパティの初期化)

    StaticMesh = ConstructorStatics.Object0.Object;
}
クラスの参照

ConstructorHelpers::FClassFinder は指定された UClass への参照を検索します。クラスが見つからない場合は失敗したことが報告されます。

APylon::APylon(const class FObjectInitializer& ObjectInitializer)
:Super(ObjectInitializer)
{
    // Structure to hold one-time initialization (一度きりの初期化を保持する構造体)
    static FClassFinder<UNavigationMeshBase> ClassFinder(TEXT("class'Engine.NavigationMeshBase'"));
    NavMeshClass = ClassFinder.Succeeded() ?ClassFinder.Class : nullptr;
}

たいていの場合、 USomeClass::StaticClass() を使って ClassFinder の複雑さをまとめてスキップすることができます。例えば、ほとんどの状況で次のメソッドを利用できます。

NavMeshClass = UNavigationMeshBase::StaticClass();

モジュール間の参照の場合は、ClassFinder メソッドを使用した方が良いかもしれません。

コンポーネントとサブ オブジェクト

コンポーネント サブオブジェクトの作成およびアクタの階層へのアタッチも、コンストラクタの実行中に行うことができます。アクタのスポーン時に、そのコンポーネントは CDO からクローンされます。コンポーネントが毎回正しく作成、破壊、ガーベジコレクション処理されるように、 コンストラクタで作成された各コンポーネントへのポインタは、所有クラスの UPROPERTY に格納されます。

UCLASS()
class AWindPointSource : public AActor
{
    GENERATED_BODY()
    public:

    UPROPERTY()
    UWindPointSourceComponent* WindPointSource;

    UPROPERTY()
    UDrawSphereComponent* DisplaySphere;
};

AWindPointSource::AWindPointSource()
{
    // Create a new component and give it a name. (次に、スタティックメッシュのコンポーネントを作成し、名前を付けます。)
    WindPointSource = CreateDefaultSubobject<UWindPointSourceComponent>(TEXT("WindPointSourceComponent0"));

    // Set our new component as the RootComponent of this actor, or attach it to the root if one already exists. (新規コンポーネントをこのアクタの RootComponent に設定する、もしくはルートが既に存在している場合はルートにアタッチします。)
    if (RootComponent == nullptr)
    {
        RootComponent = WindPointSource;
    }
    else
    {
        WindPointSource->AttachTo(RootComponent);
    }

    // Create a second component. (2 つ目のコンポーネントを作成します。)このコンポーネントはさきほど作成したコンポーネントにアタッチされます。
    DisplaySphere = CreateDefaultSubobject<UDrawSphereComponent>(TEXT("DrawSphereComponent0"));
    DisplaySphere->AttachTo(RootComponent);

    // Set some properties on the new component. (新規コンポーネントにプロパティを設定します。)
    DisplaySphere->ShapeColor.R = 173;
    DisplaySphere->ShapeColor.G = 239;
    DisplaySphere->ShapeColor.B = 231;
    DisplaySphere->ShapeColor.A = 255;
    DisplaySphere->AlwaysLoadOnClient = false;
    DisplaySphere->AlwaysLoadOnServer = false;
    DisplaySphere->bAbsoluteScale = true;
}

親クラスに属するコンポーネントの修正は通常必要ありません。ただし、親クラスによって作成されたコンポーネントを含め、アタッチされたすべてのコンポーネントの現在のリストが 利用できます。ルート コンポーネントを含め、USceneComponentGetAttachParentGetParentComponentsGetNumChildrenComponentsGetChildrenComponentsGetChildComponent を 呼び出してみてください。