PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

UE4 Moduleについて

環境

Unreal Engine 4.17.2
Visual Studio Community 2015


概要

今回はPluginではなく、Moduleの分割でビルドを早くしたりコードの結合度を下げたいとかそういう人向けです。
Plugin開発についての知見も得られると思います。


Moduleの追加

先ずは新規にModuleを追加する方法を説明します。
基本的にはTemplateからのPluginの作成とかで勝手に作られるのですが
BlankでC++プロジェクトを作成した状態から開始します。

今回はModuleSandboxというプロジェクトに
BattleSystemという新規Moduleを追加します。

手順①
追加したいModule名のフォルダをProject/Source以下に作成する。
f:id:PaperSloth:20180305213201p:plain

手順②
[ProjectName].Build.cs
[ProjectName].cpp
[ProjectName].h
を手順①で作成したModuleNameフォルダにコピー
f:id:PaperSloth:20180305213402p:plain

手順③
コピーした
[ProjectName].xxxを
[ModuleName].xxxに変更する
f:id:PaperSloth:20180305213657p:plain

手順④
[ModuleName].Build.csのProjectNameの箇所をModuleNameに変更する

using UnrealBuildTool;

// 変更前:public class ModuleSandbox : ModuleRules
public class BattleSystem : ModuleRules
{
	// 変更前:public ModuleSandbox(ReadOnlyTargetRules Target) : base(Target)
	public BattleSystem(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

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

手順⑤
[ModuleName].cppのProjectNameの箇所をModuleNameに変更
Moduleのクラスを追加し、Moduleの種類を指定する。

// 変更前:#include "ModuleSandbox.h"
#include "BattleSystem.h"
#include "Modules/ModuleManager.h"

// 追加
class FBattleSystemModule : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return true;
    }
};
// 追加終わり

// 変更前:IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ModuleSandbox, "ModuleSandbox" );

// 今回はGameModuleとして追加する。
// GameModule, Moduleの違いは次の章で解説します。
IMPLEMENT_GAME_MODULE(FBattleSystemModule, "BattleSystem");
// IMPLEMENT_MODULE(FBattleSystemModule, "BattleSystem");

IMPLEMENT_PRIMARY_GAME_MODULEはプロジェクト中に1つしか存在しません。
追加するModuleは全てPRIMARYを外す必要があります。

手順⑥
ExtraModuleNamesに追加Moduleを加える。
[ProjectName].Target.cs
[ProjectName].Editor.Target.cs
上記2つに変更を加えます。変更方法は同じです。

using UnrealBuildTool;
using System.Collections.Generic;

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

		// 変更前:ExtraModuleNames.AddRange( new string[] { "ModuleSandbox" } );
		ExtraModuleNames.AddRange( new string[] { "ModuleSandbox", "BattleSystem" } );
	}
}


手順⑦
uprojectにModuleを登録する。
[ProjectName].uprojectをテキストエディタで開き、追加Moduleの設定を加える。

{
    "FileVersion": 3,
    "EngineAssociation": "4.17",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "ModuleSandbox",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        },
        // 追加分
        {
            "Name": "BattleSystem",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        }
        // 追加終了
    ]
}

手順⑧
Solutionの更新
.uprojectを右クリックし、Generate Visual Studio project filesでslnの更新を行う。
f:id:PaperSloth:20180305215306p:plain


エラーが出なければこれでModuleの追加が出来ています。
ソリューションを開いてSource以下に追加されていれば成功です。
f:id:PaperSloth:20180305215549p:plain


GameModuleとModuleの違い

この辺りは少し面倒な話でUE4 C++本でも
プラグインに対してのホットリロードには難ありというのが現時点での正直な感想です"と書かれています。

私はあまり不便だと感じていませんが、それはさて置き
先に結論だけ述べます。
GameModuleの場合はHotReloadの際にビルドに含まれます。
Moduleの場合はCompileボタンを押してもHotReloadが行われません。

Moduleの場合はWindow -> Developer Tools -> Modulesウィンドウから行う必要があります。
f:id:PaperSloth:20180305221125p:plain
Recompileボタンを押下することでHotReloadが行われます。

他にも幾つか違いがあるとは思いますが、私が触ってみて分かった範囲について記述させていただきました。


PluginでもProjectの別Moduleの場合でも開発中にModuleとGameModuleを切り替えることが可能です。
ビルド時間をとりあえず短縮したい場合はModuleとして作成すれば良いと思われます。
Pluginの場合はModuleとして作成されます。

GameModuleの場合

#include "[ModuleName].h"
#include "Modules/ModuleManager.h"

class F[ModuleName]Module : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return true;
    }
};
IMPLEMENT_GAME_MODULE(F[ModuleName]Module, "[ModuleName]");


Moduleの場合

#include "[ModuleName].h"
#include "Modules/ModuleManager.h"

class F[ModuleName]Module : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return false;
    }
};
IMPLEMENT_MODULE(F[ModuleName]Module, "[ModuleName]");