Unreal Engine 4 でAndroidのプロジェクト作成及びAndroidのプラグイン開発

Android用のUnreal Engine向けのプラグイン開発に携わったので、
インストールからAndroidで実行及びAndroid JAVA層へのアクセスするプラグイン開発方法。

# 1.Unreal Engine 4のインストール
[Unreal Engine をインストールする](https://docs.unrealengine.com/ja/GettingStarted/Installation/index.html)を参考にUnreal Engineをインストールします。

# 2.Unreal Engineのプロジェクトを作成する。
Unreal Engineを起動するとテンプレート選択画面が現れると思います。
![ProjectBrowserSelectTemplate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/eba695a5-f9a4-4a78-a393-fd1a2c1af6d3.png)

Blankを選び次へを選択するとプロジェクトの設定画面に遷移します。
プロジェクトの設定を下記のように設定します。

設定

- C++
- スケーラブルな3D・2D
- レイトレーシング無効
- モバイル／タブレット
- スターターコンテンツ無し

名前

- HelloProect

![ProjectBrowserSelectSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/0b6d4a5c-92f2-78db-6fb2-0139d712a622.png)

プロジェクトの作成を選択するとHelloProectが作成されHelloProectが起動されます。
![HelloProect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/845c041e-00d1-5505-75aa-047fabf4198c.png)

# 3.Unreal EngineのプロジェクトをAndroidに対応させる
設定を開くと次の画面が起動します。
![HelloProectSettingProjectDescription.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/eed1432b-dac8-10fd-cef6-9f66a5bdc460.png)

プラットフォーム - Android欄を選択すると次の画面になります。
![HelloProectSettingPlatformAndroid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/987f5463-ab8c-8ebd-a2b5-8951df2c7534.png)

プロジェクトはAndroidプラットドームに対応していませんの個所の今すぐ設定を選択すると下記のように切り替わります。
またmin SDK及びtarget SDK等も指定できます。
![HelloProectSettingPlatformAndroidInput.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/836d1cce-b7c4-bfbc-070d-1485253fc210.png)

この状態で実行するとAndroid端末上で動くと思います。

# 4.Unreal EngineでAndroidのJAVA層を呼び出すためのプラグイン作成
まずプラグインの設定メニューを開きます。
![HelloProectPlugin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/d471e779-6611-1aae-1e81-3cb492dd7864.png)

New Pluginを選択することで新しいプラグインの作成メニュー画面が表示されます。
![HelloProectNewPlugin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/117da7ce-b882-bf70-1e84-e3b97c252f4c.png)

空のプラグインを選択し、プラグインを名前を記入し、Create Pluginを選択すると新しいプラグインが作成されます。
下記のようにプラグインメニューの一番下に作成したプラグインがあると思います。
プラグインを有効化しましょう。
![HelloProectPluginOther.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170755/2c3507e1-118f-fb97-ddb7-24978f200e1b.png)


UI上で出来る事は以上になると思います。

# 5.Unreal EngineでAndroidのJAVA層を呼び出すためのプラグイン開発
## 5.1.Unreal Engineからプラグインモジュールを取得する

```root/Source/HelloProjectGameModeBase.cpp
    // 例
    FHelloWorldPluginModule& module = FHelloWorldPluginModule::Get();
```

## 5.2.プラグイン側の設定
upluginの設定

```root/Plugins/HelloWorldPlugin/HelloWorldPlugin.uplugin
{
	"FileVersion": 3,
	"Version": 1,
	"VersionName": "1.0",
	"FriendlyName": "HelloWorldPlugin",
	"Description": "Sample plugin to call Android JAVA layer",
	"Category": "Other",
	"CreatedBy": "Sacred Sanctuary",
	"CreatedByURL": "",
	"DocsURL": "",
	"MarketplaceURL": "",
	"SupportURL": "",
	"CanContainContent": true,
	"IsBetaVersion": false,
	"IsExperimentalVersion": false,
	"Installed": false,
	"Modules": [
		{
			"Name": "HelloWorldPlugin",
			"Type": "Runtime",
			"LoadingPhase": "Default",
			"WhitelistPlatforms": [ "Android", "IOS", "Mac", "Win32", "Win64" ]
		}
	]
}
```

プラグインのインクルード設定

```root/Plugins/HelloWorldPlugin/Source/HelloWorldPlugin/HelloWorldPlugin.Build.cs
if (Target.Platform == UnrealTargetPlatform.Android) {
    // Plugin settings for Android
    PrivateIncludePaths.AddRange(
        new string[] {
            "HelloWorldPlugin/Private/Android",
        });
    string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);
    AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(PluginPath, "HelloWorldPlugin_APL.xml"));
}
```

Androidアプリ側のAndroidManifest.xmlやbuild.gradleの設定

```root/Plugins/HelloWorldPlugin/Source/HelloWorldPlugin/HelloWorldPlugin_APL.xml
<?xml version="1.0" encoding="utf-8"?>
<!--AndroidCamera plugin additions-->
<root xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- init section is always evaluated once per architecture -->
    <init>
        <log text="HelloWorldPlugin init"/>
        <!-- currently supports all architectures -->
    </init>

    <!-- optional updates applied to AndroidManifest.xml -->
    <androidManifestUpdates>
        <!-- How to add Permission -->
        <addPermission android:name="android.permission.INTERNET"/>

        <!-- How to add Activity -->
        <setElement result="HelloActivity" value="activity" />
        <addAttribute tag="$HelloActivity" name="android:name" value="jp.sacredsanctuary.helloworld.HelloActivity" />
        <addElement tag="application" name="HelloActivity" />

        <!-- How to add Service -->
        <setElement result="HelloService" value="service" />
        <addAttribute tag="$HelloService" name="android:name" value="jp.sacredsanctuary.helloworld.service.HelloService" />
        <addAttribute tag="$HelloService" name="android:enabled" value="true" />
        <addElement tag="application" name="HelloService" />
    </androidManifestUpdates>
    
    <!-- optional additions to proguard -->
    <proguardAdditions>
        <insert>
            -keep class jp.sacredsanctuary.helloworld.service.HelloFromNative {
                *;
            }
        </insert>
    </proguardAdditions>

    <!-- optional files or directories to copy to Intermediate/Android/APK -->
    <resourceCopies>
        <copyDir src="$S(PluginDir)/ThirdParty/Android/Java" dst="$S(BuildDir)" />
    </resourceCopies>

    <!--
    <buildscriptGradleAdditions>
        <insert>
            dependencies {
            }
        </insert>
    </buildscriptGradleAdditions>
    -->

    <!--  optional base build.gradle additions -->
    <buildGradleAdditions>
        <insert>
            android
            {
                // ADD COMPATIBILITY OPTIONS TO BE COMPATIBLE WITH JAVA 1.8
                compileOptions {
                    sourceCompatibility = JavaVersion.VERSION_1_8
                    targetCompatibility = JavaVersion.VERSION_1_8
                }
            }

            // ADD SENTRY ANDROID AS A DEPENDENCY
            dependencies {
                implementation "androidx.annotation:annotation:1.1.0"
            }
        </insert>
    </buildGradleAdditions>
</root>
```

`<init></init>`で初期処理に行い事を記述できます。
`<androidManifestUpdates></androidManifestUpdates>`にAndroidManifest.xmlへ追加する項目を記述出来ます。
`<resourceCopies></resourceCopies>`にソースコード元とソースコードのコピー先
`<proguardAdditions></proguardAdditions>`にproguard-rules.proへ追加する項目を記述出来ます。
`<buildscriptGradleAdditions></buildscriptGradleAdditions>`にbuild.gradleへの追加する項目を記述出来ます。
`<buildGradleAdditions></buildGradleAdditions>`にapp/build.gradleへの追加する項目を記述出来ます。

# 6.ソースコード
Unreal Engine側からAndroid Java側へのアクセスは`JNI`を使用して呼び出す必要があります。
`JNIEnv`を取得するには下記のようにして取得できます。

```c++
JNIEnv* env = FAndroidApplication::GetJavaEnv();
```

また、Android Java側では至る所で`Context`を使用しますが、`Context`についてもUnreal Engineのものを使用します。
取得方法は下記のようになります。

```c++
jobject gameActivity = FAndroidApplication::GetGameActivityThis();
```

Android Java側の呼び出し例

```c++
// JAVA側のクラス[HelloFromNative]インスタンスを取得
jclass mClazz = FAndroidApplication::FindJavaClassGlobalRef("jp/sacredsanctuary/helloworld/service/HelloFromNative");

// 取得したクラスの呼び出すメソッド[startHelloActivityFromNative]を取得
jmethodID method = FJavaWrapper::FindStaticMethod(env, mClazz, "startHelloActivityFromNative", "(Landroid/content/Context;)V", false);

// HelloFromNativeクラスのstartHelloActivityFromNativeメソッドを呼び出す
env->CallStaticVoidMethod(mClazz, method, mContext);
```

これでJava側の呼び出しが可能となり、Java側を使用してAndroidのAPIを呼び出すことが出来るようになります。
Java側で処理した内容はJNIを使用してUnreal Engine側へ返すことも可能です。

今回の内容の[ソースコード](https://github.com/sacred-sanctuary/UE4PluginSample)になります。
内容はUnreal Engine側からJava層を呼び出して、Java層でActivityを開始するという至ってシンプルなものとなります。