Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
21
Help us understand the problem. What is going on with this article?
@strv13570

ちょっとだけくわしくUE4 モジュールの話【解説編】

More than 1 year has passed since last update.

趣旨

UE4は本当にいろいろなことができますが、特定のトピックに対する情報のまとまった記事というのがあまりない気がします。
手順書的な記事は結構あったりするのですが、そのものに対する解説記事的なのは更にない気がします。なので、UE4の勉強がてら書いてみました。この記事を読むときは、手元で同じように実装を読んでいってもらえるとわかりやすいかと思います。

僕は趣味でUE4をやっているへっぽこ学生なので、なにか間違っていたら遠慮なくご指摘いただけると、僕とこの記事を今後見る人が助かります。

第一回は、モジュールについての話です。

環境

  • エンジンバージョン: 4.22.3
  • プロジェクト名: MyProject
  • OS: Windows 10

プロジェクト名や環境依存の名前などは適時読み替えてください。

モジュール

UE4においてモジュールとは、エンジンを構成する構成単位そのものです。

エンジン自体が大きな塊ではなく、機能ごとにモジュールという形で分割されています。また、UE4においてはプラグインもモジュールとして実装されます。

さらに、エンジンを用いて開発されるC++プロジェクトのソースも、新たなモジュールとして扱われます。
以下にモジュールのイメージ図を示します。
UE4Archtecture.png

この図を見ていただくと、エンジンとして持っている大量のモジュールの中に、C++プロジェクトで実装した内容が新規モジュールとして追加されている構造がわかりやすいと思います。このC++プロジェクトで実装を行うモジュールのことを、ゲームモジュールといいます。特に、C++プロジェクト作成時にデフォルトで作成されるゲームモジュールのことをプライマリゲームモジュールと呼び、プライマリゲームモジュールはプロジェクトに1つしか存在できません。

UE4におけるビルドは、モジュール単位で行われます。ビルドを行うと、1モジュールにつき1つの.dll、つまりダイナミックリンクライブラリが出力されます。こうしてビルドされたダイナミックリンクライブラリを読み込んでいき、結果としてUE4という大きなシステムが動作していることになります。

UE4はこのようなアーキテクチャによって、Unreal C++プログラマがお世話になっているホットリロードを実現しています。本来C++のような事前コンパイルを必要とする言語は、対象のプログラムを起動したままソースを再コンパイルして変更を反映するなどもちろんできません。しかし、UE4ではこのようなモジュール単位でのビルドの仕組みがあるため、UE4という大きな視点ではプログラムを起動したまま、変更のあったモジュールのみリビルドし、変更前に読み込んでいたdllをアンロード、変更後のdllをホットリロードしているのです。モジュール様様、動的リンク様様ですね。

なお、モジュールのビルド結果であるdllの場所は、UE4上でmodule listというコマンドを打つと簡単に確認できます。
image.png

ゲームモジュールを追加する意義

プライマリゲームモジュール以外にゲームモジュールを追加作成すべきシチュエーションの例を挙げておきます。
プロジェクト上で独自のアセットを実装し、ゲーム上で利用するとします。この独自アセットには、エディタ上での作成機能、インポート機能、独自エディタが実装されています。

この時、アセット自体に含まれるプロパティやメソッドは、ゲーム上に必要な情報です。しかし、エディタ機能やインポート機能などは、ゲーム上に必要な情報でしょうか?これはエディタを用いて制作を行う過程において必要な実装情報であって、Shippingビルドを行ってユーザーにゲームを公開する際には含む必要のない情報です。しかしそんな都合はビルドシステムもコンパイラも察してくれないため、1つのモジュールに愚直に実装してしまうと、エディタにしか必要のない情報をゲームに含めてしまいます。

ここで新規モジュールの登場です。UE4のビルドシステムには、ビルド設定ごとにビルドに含めるモジュールを変更する機能が用意されています。このため、新規モジュールを用意しカスタムエディタや新規作成機能部分のみをこちらに実装、Editorビルド時にのみ対象とするよう設定すれば、Gameビルド(Shippingビルドなどのこと)時にはゲームに必要な実装のみを含めることができるようになります。

また、モジュールを分割することでビルド時のリンク速度を上げることができます。ただし、ゲーム実装自体を分割すると、リンク速度の向上とトレードオフとして実行時に読み込むDLLの数が増えるため、ゲームプレイにおいては悪影響を及ぼす恐れがあり(ますと公式に書いてあり)ます。実際の影響の程度は検証を行っていないためわかりませんが、そこまで大きな影響はなさそうな気もします。というかGameビルド時にはサードパーティ製以外のバイナリは実行ファイルにまとめられているはずですが、モジュール数は影響するんでしょうか?

コードの結合度を下げて、使い回しを楽にしやすいという利点もあります。

モジュールの構成

C++プロジェクトを作成すると、Source以下はこんな構成になっていると思います。

-- Source
    |-- MyProject
    |   |-- MyProject.Build.cs
    |   |-- MyProject.cpp
    |   `-- MyProject.h
    |-- MyProject.Target.cs
    `-- MyProjectEditor.Target.cs

ここでMyProjectというディレクトリの中に入っているのが、C++プロジェクト作成時に生成されるプライマリゲームモジュールです。
MyProject.cpp, MyProject.hといったC++ファイルはモジュールについての実装を行うために用意されているファイルです。

モジュールファイル

ここで重要なのはMyProject.Build.csというC#ファイルです。このファイルは、モジュールに関する各種設定を行うためのファイルで、モジュールファイルと呼ばれるものです。UE4でビルドを行う際には、ビルドツールがモジュールファイルを探索し、モジュールを認識することで、モジュールに含まれるコードが適切な設定でビルドされます。

では、デフォルトで生成されているMyProject.Build.csを覗いてみましょう。

MyProject.Build.cs
// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;

public class MyProject : ModuleRules
{
    public MyProject(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

        PrivateDependencyModuleNames.AddRange(new string[] {  });
    }
}

デフォルトでは、そこまで複雑な設定は行われていません。PublicDependencyModuleNamesというメンバ変数の配列になにか文字列を追加しているところと、PCHUsageというメンバ変数になにか代入しているところだけです。順に説明します。

配列PublicDependencyModuleNamesは、このMyProject.Build.csが示すモジュールが依存するモジュールの名前をセットし、モジュール内で外部の依存するモジュールを利用できるように設定する変数です。依存しているモジュールがここに含まれていないと、ビルド時にエラーが出ます。

PCHUsageというのは、IWYU(Include-What-You-Use)というシステムの利用方法について設定する変数です。IWYUについてはここでは解説しませんが、簡単に言えば依存関係をいい感じに整理することでビルド速度などを早くしてくれる機能です。詳細は以下の記事とドキュメントを御覧ください。

IWYUでコーディングしよう
http://miyahuji111.hatenablog.com/entry/2017/04/03/081222

IWYU リファレンス ガイド
https://api.unrealengine.com/JPN/Programming/UnrealBuildSystem/IWYUReferenceGuide/index.html

ここではこの2つしか設定が行われていませんが、他にも多くの設定項目が存在します。
設定可能な項目については以下の記事とドキュメントが詳しいです。

ModuleRules(XXX.build.csファイル)について
https://qiita.com/takayashiki2/items/db995c558024c3db8223

アンリアル ビルド システムのモジュール ファイル
https://api.unrealengine.com/JPN/Programming/UnrealBuildSystem/ModuleFiles/index.html

設定項目に代入すべき列挙型の中身が不明だったりして困惑することがありますが、そんなときにはエンジンソースに含まれているビルドツールのソースを読みに行きましょう!!

ターゲットファイル

Sourceの直下にあるMyProject.Target.cs,MyProjectEditor.Target.csの2つのC#ファイルもやはりビルドに関する設定を行うファイルで、ターゲットファイルと呼ばれるものです。これらの中では、上記のMyProject.Build.cs等で発見されたモジュールのうち、”どれを”ビルド対象にするかという設定を行います。

つまり、モジュールとして完璧な形のコードを作成しても、ここに設定を追記しなければモジュールはビルドに含まれないということです。

では、なぜこのファイルは2つ生成されるのでしょうか。ここで、Gameビルド時とEditorビルド時で含まれるモジュールを変えられるというところに話が戻ります。
MyProjectEditor.Target.csに設定したモジュールはEditorビルド時にビルド対象となり、MyProject.Target.csに設定したモジュールはGameビルド時にビルド対象となるといったように、使い分けることができます。
試しに、MyProject.Target.csの方を見てみます。

MyProject.Target.cs
// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;
using System.Collections.Generic;

public class MyProjectTarget : TargetRules
{
    public MyProjectTarget(TargetInfo Target) : base(Target)
    {
        Type = TargetType.Game;

        ExtraModuleNames.AddRange( new string[] { "MyProject" } );
    }
}

まず、Typeという変数にこのMyProject.Target.csによって行われるビルドの種類を指定しています。ちなみに、ビルドツールのコードではこのTargetTypeという型は以下のように列挙型として定義されています。

ビルドツールより抜粋(TargetRules.cs)
    public enum TargetType
    {
        Game,
        Editor,
        Client,
        Server,
        Program,
    }

生成されるMyProject.Target.cs,MyProjectEditor.Target.csTargetTypeGame,Editorが設定されたターゲットファイルですが、ビルドツールを見るとまだ他にもタイプがあるようです。

アンリアルビルド システムのターゲット ファイル
https://api.unrealengine.com/JPN/Programming/UnrealBuildSystem/TargetFiles/index.html

公式から引用させていただくと、それぞれこんな意味があるそうです。

Game - クック済みデータの実行を要求するスタンドアロン ゲームです。
Client - Game と同じですが、サーバー コードは含まれません。ネットワーク ゲームに役立ちます。
Server - Game と同じですが、クライアント コードは含まれません。ネットワーク ゲームのデディケイテッド サーバーに役立ちます。
Editor - アンリアル エディタを拡張するターゲットです。
Program - アンリアル エンジンに加えてビルドされているスタンドアロン ユーティリティ プログラムです。

なお、引用元に重大な誤植があったため、引用文は一部修正を加えています。

これだけビルドタイプがあるにも関わらず、デフォルトでは2種類分しか生成されないということは、自分でターゲットファイルを追加することができるということです。ターゲットファイルの作成については流石に話がそれすぎるのでここでは解説しません。ちなみに、同じビルドタイプのターゲットファイルを複数作成しても、1つしか認識されませんのでご注意ください。

次に、ExtraModuleNamesです。ここが重要なところで、ビルドターゲットにするモジュールの識別名をセットする配列です。

デフォルトではゲームモジュールがプライマリゲームモジュール1つしかないので1つしか指定されておらず、MyProject.Target.csMyProjectEditor.Target.csどちらも同じ設定になっていますが、前述のような都合でモジュールを追加した場合は大変便利です。

IDEなどからビルドを行う際には、ターゲットファイルのクラス名が併記されたビルド設定が生成されているので、対応するものを選択すれば、どちらの設定でビルドを行うか指定することができます。

.uprojectファイル

UE4のプロジェクト直下に生成される.uprojectファイルにもモジュールに関する設定項目が存在します。
今回生成されたMyProject.uprojectを覗いてみます。

MyProject.uproject
{
    "FileVersion": 3,
    "EngineAssociation": "4.22",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "MyProject",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        }
    ]
}

モジュールに関する項目は"Modules"の箇所です。ここには、プロジェクトで定義したモジュールを設定します。モジュールファイルやターゲットファイルには基本的にビルドに関する設定を行ってきましたが、.uprojectに記述するのは起動時の読み込み対象のモジュールの識別名です。ここに記述しなくてもビルド対象にすることはできますが、起動時の読み込みやホットリロード等の対象にはなりません。

また、識別名だけでなく、そのモジュールを読み込む動作タイプと、読み込みタイミングも設定することができます。それぞれ以下のようになっています。

Type

読み込み時タイプ名 概要
Runtime 通常実行時には読み込む
RuntimeNoCommandlet 実行時、コマンドレットでない場合読み込む
RuntimeAndProgram 実行時かつビルドターゲットがProgarmである場合読み込む
CookedOnly クックビルドされた後の場合読み込む
Developer デベロッパツールをサポートしたエンジン上でのみ読み込む
Editor エディタビルドの場合読み込む
EditorNoCommandlet エディタビルドかつコマンドレットでない場合読み込む
Program Programビルドされた場合のみ読み込む
ServerOnly Serverビルドされた場合のみ読み込む
ClientOnly Clientビルドされた場合のみ読み込む

LoadingPhase

読み込み順 ローディングフェーズ名 タイミング
1 EarliestPossible 可能な限り最も早く読み込みます
2 PostConfigInit エンジン設定の初期化後に読み込みます
3 PreEaryLoadingScreen PreLoadingScreenよりも前に読み込みます
4 PreLoadingScreen 画面が起動する前に読み込みます
5 PreDefault Defaultの前に読み込みます
6 Default 通常のモジュールロードタイミングで読み込みます
7 PostDefault Defaultの後に読み込みます
8 PostEngineInit エンジンの初期化処理が終了したあとに読み込みます

なお、モジュールを追加する際には以下のようにします。

MyProject.uproject
{
    "FileVersion": 3,
    "EngineAssociation": "4.22",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "MyProject",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        },
        {
            "Name": "NewModule",
            "Type": "Editor",
            "LoadingPhase": "Default"
        }
    ]
}

なぜビルド設定がC#?

設定ファイルがC#なのは、UE4のビルドツールが本体がC#で書かれていることに由来します。

モジュールファイルや後述のターゲットファイルといったC#による設定ファイルは、Generate ~などのメニューからプロジェクトを生成する際にビルドが行われ、そのプロジェクトにおけるモジュールのビルドルールとなるDLLが生成されます。

これをビルドツール本体が動的リンクすることで、C++プロジェクトのゲームモジュールを含めた適切なビルドを行っているんだと思います。(間違っていたらごめんなさい)
謎のビルドエラーが起こったときにIntermediateを消すと動くのはここにも一因があると思われます。

コンテキストメニュー
image.png

生成されたビルドルールのDLL
image.png

単なる設定ファイルとしてだけでなく、ビルド時に行いたい処理なども記述できるので結構便利です。

モジュールの実装を確認してみる

ここまでモジュールの設定方法とビルドについて詳しく見てきましたが、肝心のモジュールの宣言方法に触れていません。モジュール自体はC++実装なのですから、モジュールの宣言もC++で行ってやる必要があります。

改めてモジュールの構成を示します。

-- Source
    |-- MyProject
    |   |-- MyProject.Build.cs
    |   |-- MyProject.cpp
    |   `-- MyProject.h
    |-- MyProject.Target.cs
    `-- MyProjectEditor.Target.cs

C#のファイルたちは皆設定を行うファイルだったので、モジュールの実装を行うのは残ったC++ファイル2つ、MyProject.hMyProject.cppです。

とりあえず、デフォルトの状態のこの2つのファイルを見てみましょう。

MyProject.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
MyProject.cpp
// Fill out your copyright notice in the Description page of Project Settings.

#include "MyProject.h"
#include "Modules/ModuleManager.h"

IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, MyProject, "MyProject" );

もはや無です。というか、実装らしき箇所が1箇所しかありません。
これはこのC++プロジェクトが生成した、プライマリゲームモジュールをコードであるため、IMPLEMENT_PRIMARY_GAME_MODULEというマクロで、そのマクロ名の通りプライマリゲームモジュールが実装されています。
では、このマクロを含め、モジュール定義のためのマクロたちを紹介します。

IMPLEMENT_PRIMARY_GAME_MODULE

これはたった今登場した、プライマリゲームモジュール実装のためのマクロです。
プライマリゲームモジュールはプロジェクト上に1つしか実装できないため、必然的にこのマクロもプロジェクトを通して1つのモジュールにしか書くことはありません。逆に、これがないとShippingビルドなどのモノリシックビルド時にビルドが通らないんじゃないかと思います。(試してないです)

IMPLEMENT_PRIMARY_GAME_MODULE(ModuleImplClass, ModuleName, GameName)

このマクロは、第一引数にモジュールを実装したクラス、第二引数にモジュール名、第三引数にゲーム(プロジェクト)名を取ります。

第一引数に渡すモジュールを実装したクラスは、後述のIModuleInterfaceというインターフェイスを継承して実装したモジュールクラスを渡すことができます。

第二引数に渡すモジュール名は、他のモジュールやエンジン上からこのモジュールを識別する際の名称として使われる文字列を決定します。実装クラスと同じ名前である必要はありません。例えば、ターゲットファイルのExtraModuleNamesに渡していたのはここで決定される識別名です。

第三引数はゲーム(プロジェクト)名を渡すのですが、実装を覗くと、この第三引数はもう利用されていません。昔はここに渡したゲーム名を利用した処理が行われていたようなのですが、現在は.uprojectファイルから自動的にプロジェクト名をパースしてくるようで、コード上でも廃止予定とされています。おそらく残っているのは後方互換性のためでしょう。現状も値を渡さなければエラーが出ますが、実際は文字列さえ渡しておけば良いと思われます。

IMPLEMENT_GAME_MODULE

こちらはゲームモジュールを実装するためのマクロです。ゲームモジュールはプライマリゲームモジュールと異なり、プロジェクト上に複数個実装することが可能なため、このマクロも複数のモジュールに書くことができます。ゲームコードを含むモジュールはこれで実装しましょう。

IMPLEMENT_GAME_MODULE(ModuleImplClass, ModuleName)

第一引数と第二引数はプライマリゲームモジュールの場合と同じで、実装クラスとその識別名を与えます。
(プライマリゲームモジュールの第三引数は廃止予定だったので、中身は異なりますが渡しているものは実質同じですね。)

IMPLEMENT_MODULE

こちらはシンプルなモジュールを実装するためのマクロです。ゲームコードを含まないモジュールはこのマクロで実装されます。

IMPLEMENT_MODULE(ModuleImplClass, ModuleName)

渡している引数はIMPLEMENT_GAME_MODULEと同様です。

ここで使われているIMPLEMENT_PRIMARY_GAME_MODULEというマクロはプライマリゲームモジュールを実装する(implementなので)マクロなのですが、実は初期状態では、はじめから用意されているFDefaultGameModuleImplという最低限のクラスをゲームモジュールとして利用(代用?)しているだけです。もっとモジュールクラス自体に機能を持たせるために用意されている手段は後述しますので、とりあえずFDefaultGameModuleImplの中身を見てみます。

モジュール実装マクロの余談

この節には不確定要素が多いです。
ここまで紹介してきたモジュール実装のためのマクロたちですが、実装を見るとこれらはビルド方式によって内部処理が変動しているようです。プラットフォームなどによる変動はもちろんあるのですが、最も大きいのがMonolithic(一枚岩)Modular(まとまりごと)という2種類のリンクタイプのによる違いです。
リンクがわからない方は詳しくは調べてほしいのですが、ざっくり言えばコンパイルした複数のファイルや必要なライブラリを正しく関連付ける作業です。

この2つのリンクタイプによって生まれるビルドの結果は「ビルド結果として得られるバイナリが実行ファイルにまとまっているか、多量の.dllなどに分散しているか」の違いがあります。

パッケージングのビルドやGameビルド(Shippingビルド)などは実行ファイルとしてバイナリがまとまるためMonolithicで行われて、Editorビルドの際などは.dllとしてバイナリがモジュールごとに生成されるためModularで行われていることがわかります。

そして、Modularでリンクが行われる際、前述のIMPLEMENT_MODULE,IMPLEMENT_GAME_MODULE,IMPLEMENT_PRIMARY_GAME_MODULEの3つのモジュール実装マクロの内部処理に差はありません

すべての実装マクロが、リンクタイプがModular時のIMPLEMENT_MODULEの処理へと渡されています。これは推測ですが、ModularすなわちEditor上で開発を行っている際には、エンジンというソフトウェアの管理はエンジン側にあるため、ゲームモジュールを含めすべてのモジュールは内部的には同等のプログラムとして扱われているのではないでしょうか。確かに、Gameビルド時のようにプライマリゲームモジュール中心で動作していたら、それ自身のホットリロードは困難な気がします。(怪しいです。違ったら指摘してほしいところNo.1です)

一方Monolithic時の実装はIMPLEMENT_PRIMARY_GAME_MODULEが独自の複雑な処理に変わったり、IMPLEMENT_MODULEの処理からホットリロードの処理が外されたりしています。しかし、実装を見る限りではIMPLEMENT_MODULEIMPLEMENT_GAME_MODULEの間にはこちらでも差異は見られない気がします……。というか、IMPLEMENT_GAME_MODULEの実装は常に以下になっています。

ModuleManager.hより抜粋
#define IMPLEMENT_GAME_MODULE( ModuleImplClass, ModuleName ) \
    IMPLEMENT_MODULE( ModuleImplClass, ModuleName )

うーん。各所の説明を読むと別のものとされているのですが、表面上のものなのでしょうか?有識者の方、教えて下さい。

モジュールクラスについて

ここでは、モジュール実装マクロに渡すモジュールクラスについて触れます。
例として、デフォルトで生成されたIMPLEMENT_PRIMARY_GAME_MODULEに渡されていたFDefaultGameModuleImplクラスの実装に注目しましょう。

ModuleManager.hより抜粋
class FDefaultModuleImpl
    : public IModuleInterface
{ };

class FDefaultGameModuleImpl
    : public FDefaultModuleImpl
{
    virtual bool IsGameModule() const override
    {
        return true;
    }
};

ここからわかることは、IModuleInterfaceというクラス(インターフェイス?)を継承して、先程のFDefaultGameModuleImplは作られていたということです。そして、FDefaultGameModuleImplは唯一つ、IsGameModuleというboolを返すメソッドでtrueを返すというオーバーライドが実装されています。IsGameModuleはその名と型の通り、自らがゲームモジュールである場合trueを返すよう設定するためのメソッドです。

ここではこれ1つのみですが、IModuleInterfaceが宣言・定義されているModuleInterface.hの実装を覗くと、他のオーバーライド可能なメソッドを知ることができます。自分でゲームモジュールを追加で宣言する場合には、このあたりを利用することが多いため、一度目を通しておくと良いです。一応ドキュメントもありますが、実装ファイル以上の情報はありません。

IModuleInterface
https://api.unrealengine.com/INT/API/Runtime/Core/Modules/IModuleInterface/index.html

モジュール実装に多用されるメソッド

IModuleInterfaceには、設定項目的なメソッド以外にも、モジュールの実装にあたって非常に重要なメソッドがいくつか定義されています。
それらをオーバライドすることで、独自モジュール実装に利用することができます。ここでは、特に使用頻度の高いメソッドについて触れます。

StartupModule

モジュールのDLLがロードが開始された直後に呼び出されるメソッドです。モジュールのオブジェクトが作成されて真っ先に呼び出されると考えれば良いです。ここには、依存関係にあるモジュールの読み込みや、このモジュールで行う実装の初期化処理などを行うことができます。

PostLoadCallback

モジュールのロードが後に実行されるメソッドです。

ShutdonwModule

モジュールが完全にアンロードされる直前に呼び出されます。終了時処理などを行うことができます。

PreUnloadCallback

モジュールのアンロード開始前に呼び出されるメソッドです。

この4つのメソッドを以下のような実装でオーバーライドしてビルドの上、ホットリロードが行われた場合の出力を確認してみます。

実装例.cpp

/*
** このコードは単体では動作しません。また、ShutdownModuleとPreUnloadCallbackについては
** 前回ビルドの結果が出力されるため、2回以上リビルドしなければ後述の出力は得られません。
*/

class FMyProjectModule : public FDefaultGameModuleImpl
{
public:
    virtual void StartupModule() override
    {
        UE_LOG(LogTemp, Log, TEXT("StartupModule"))
    }

    virtual void PostLoadCallback() override
    {
        UE_LOG(LogTemp, Log, TEXT("PostLoadCallback"))
    }

    virtual void ShutdownModule() override
    {
        UE_LOG(LogTemp, Log, TEXT("ShutdonwModule"))
    }

    virtual void PreUnloadCallback() override
    {
        UE_LOG(LogTemp, Log, TEXT("PreUnloadCallback"))
    }
};

IMPLEMENT_PRIMARY_GAME_MODULE(FMyProjectModule, MyProjectModule, "MyProject")

ホットリロード時の出力
LogTemp: PreUnloadCallback
LogTemp: ShutdonwModule
LogTemp: StartupModule
LogTemp: PostLoadCallback

つまり、以下のような実行順で実行されるということがわかります。

実行順 メソッド名
1 StartupModule
2 PostLoadCallback
- (モジュール動作中)
3 PreUnloadCallback
4 ShutdonwModule

これらは本当によく使うため、覚えておきましょう。

まとめ

  • UE4はモジュールの集合体!
  • モジュールには種類があって、使い分ける
  • モジュールは自作できる。
  • モジュール実装のための便利なものが親として提供されているから、頑張って使いこなそう!

おわりに

粗く長くやりましたが、モジュールの宣言のされかたやビルドツールとの関わりなどが少しわかっていただけたら嬉しいです。
本当は実装例提示までやろうと思ったのですが、なんだか長くなりすぎているのと、規約上引用可能なエンジンのコード行数を超過してしまいそうなので、今回はここまでにしておきます。
気が向いたときに次を書きます。

21
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
strv13570
19歳の非実在青少年 いろいろやってる https://twitter.com/strvert

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
21
Help us understand the problem. What is going on with this article?