Unreal Plugin Language リファレンス

# はじめに

UE4でAndroid向けのプラグインを作るときにサードパーティのライブラリを追加したり設定ファイルを変更したいことがあります。
プラグインを組み込んだプロジェクトでそんな変更をするのは大変です。
そこでこれらの設定をビルドツールで行い、凝ったプラグインでも簡単に導入することができるようにしたものがUnreal Plugin Languageです。
以下では、利用例やリファレンスを紹介します。

## 追記
公式ドキュメントにも[Unreal プラグイン言語](https://docs.unrealengine.com/ja/Platforms/Mobile/UnrealPluginLanguage/index.html)が作成されています。

# 利用例

こまけーことはいいんだ、という人は利用例を見た方が早いかもしれません。
GitHubとUE4をインストールしたフォルダのどちらでも見れますので、両方のパスを記載します。

※UE4のソースにアクセスするためには登録が必要なので、[エンジンのソースコード取得とビルド手順のまとめ UE4.6改訂版](http://historia.co.jp/archives/1327)を参考にしてください。サードパーティのプラグインは登録なしに見ることができます。

* GitHubのUE4のソース
    * GearVR Plugin
        * [GearVR.Build.cs](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Plugins/Runtime/GearVR/Source/GearVR/GearVR.Build.cs#L41-L45)
        * [GearVR_APL.xml](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Plugins/Runtime/GearVR/Source/GearVR/GearVR_APL.xml)
    * GoogleVR Plugin
        * GoogleVRController Module
            * [GoogleVRController.Build.cs](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Plugins/Runtime/GoogleVR/GoogleVRController/Source/GoogleVRController/GoogleVRController.Build.cs#L63-L69)
            * [GoogleVRController_APL.xml](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Plugins/Runtime/GoogleVR/GoogleVRController/Source/GoogleVRController/GoogleVRController_APL.xml)
        * GoogleVRHMD Module
            * [GoogleVRHMD.Build.cs](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Plugins/Runtime/GoogleVR/GoogleVRHMD/Source/GoogleVRHMD/GoogleVRHMD.Build.cs#L68-L74)
            * [GoogleVRHMD_APL.xml](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Plugins/Runtime/GoogleVR/GoogleVRHMD/Source/GoogleVRHMD/GoogleVRHMD_APL.xml)
* インストールしたUE4のソース
    * GearVR Plugin

            [ENGINE INSTALL LOCATION]\Engine\Plugins\Runtime\GearVR\Source\GearVR\GearVR.Build.cs
            [ENGINE INSTALL LOCATION]\Engine\Plugins\Runtime\GearVR\Source\GearVR\GearVR_APL.xml
    * GoogleVR Plugin
        * GoogleVRController Module

                [ENGINE INSTALL LOCATION]\Engine\Plugins\Runtime\GoogleVR\GoogleVRController\Source\GoogleVRController\GoogleVRController.Build.cs
                [ENGINE INSTALL LOCATION]\Engine\Plugins\Runtime\GoogleVR\GoogleVRController\Source\GoogleVRController\GoogleVRController_APL.xml
        * GoogleVRHMD Plugin

                [ENGINE INSTALL LOCATION]\Engine\Plugins\Runtime\GoogleVR\GoogleVRHMD\Source\GoogleVRHMD\GoogleVRHMD.Build.cs
                [ENGINE INSTALL LOCATION]\Engine\Plugins\Runtime\GoogleVR\GoogleVRHMD\Source\GoogleVRHMD\GoogleVRHMD_APL.xml
* サードパーティのプラグイン
    * Tango 4 Unreal Plug-in
        * [TangoPlugin.Build.cs](https://github.com/opaquemultimedia/ProjectTangoPlugin/blob/master/Source/TangoPlugin/TangoPlugin.Build.cs#L55-L58)
        * [TangoPlugin_APL.xml](https://github.com/opaquemultimedia/ProjectTangoPlugin/blob/master/Source/TangoPlugin/TangoPlugin_APL.xml)
    * Parse Platform Plugin
        * [ParsePlatform.Build.cs](https://github.com/gameDNAstudio/ParsePlatform/blob/master/Source/ParsePlatform/ParsePlatform.Build.cs#L60)
        * [ParsePlatform_APL.xml](https://github.com/gameDNAstudio/ParsePlatform/blob/master/Source/ParsePlatform/ParsePlatform_APL.xml)

# Unreal Plugin Languageとは

Unreal Plugin LanguageはXMLベースの言語で次のような機能を提供します。

* ファイル、フォルダのコピー(主にライブラリやリソース)
* ファイルの削除
* AndroidのAndroidManifest.xml、proguard、GameActivity.javaにコード追加
* iOSのInfo.plistにコード追加

このXMLは`Build.cs`で読み込みます。

## Build.cs 

UPLの対象プラットフォームは以下の2種類です。

| プラットフォーム | ReceiptPropertyのプロパティ名 |
|----|----|
| Android | AndroidPlugin |
| iOS | IOSPlugin |

Androidの場合はGearVRやGoogleVRプラグインを真似して以下のコードでXMLを読み込めばよいでしょう。

```cs:CppTest.Build.cs
if (Target.Platform == UnrealTargetPlatform.Android)
{
    string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, BuildConfiguration.RelativeEnginePath);
    AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", Path.Combine(PluginPath, "CppTest_APL.xml")));
}
```

## Unreal Plugin Languageのリファレンス

現状、リファレンスは[UnrealPluginLanguage.cs](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Source/Programs/UnrealBuildTool/System/UnrealPluginLanguage.cs)にコメントで記述されたものだけなので、これを元に解説していきます。

### 空定義のXML

```xml
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ここに追加していく -->
</root>
```

### デバッグ

```xml
<trace enable="true"/>
```

ログを有効にするかどうか設定できます。

---

```xml
<dumpvars/>
```

XMLの中で設定した変数の値をログに出力します。

---

```xml
<log text="..."/>
```

textで指定したメッセージをログに出力します。
各セクションで出力すると動作確認しやすくなります。

## 変数

* `$B(name)` : boolean 
* `$I(name)` : integer
* `$S(name)` : string
* `$E(name)` : XMLのelement

変数`name`をそれぞれの型の値として参照できます。

## 定義済みの変数

* `$S(Architecture)` : アーキテクチャ名 (`armeabi-armv7a`, `arm64-v8a`, `x86`, `x86_64`)
* `$S(PluginDir)` : XMLファイルが読み込まれたフォルダ名
* `$S(EngineDir)` : エンジンのフォルダ名
* `$S(BuildDir)` : プラットフォーム毎のビルドフォルダ名 (Intermediateフォルダ内)
* `$S(ProjectDir)` : プロジェクトのフォルダ名
* `$B(Distribution)` : Distributionビルドならtrueが設定されます。
* `$S(Output)` : 後述するセクション毎にその内容が設定されるので、動的に変更することができます。

## 共通のセクション

### init

```xml
<init></init>
```

アーキテクチャ毎に1回だけ実行されるセクションです。
変数を定義、操作したり`<log text="..."/>`でログを出したりできます。

## Android用のセクション

以下のセクションは`ReceiptProperty()`で`AndroidPlugin`を指定すると適用されます。

### androidmanifestUpdates

```xml
<androidManifestUpdates>
    <addPermission android:name="..."/>
    <addFeature android:name="..."/>
    <addLibrary android:name="..."/>

    <addElement tag="..." name="..."/>
    <removeElement tag="..."/>
    <addElements tag="..."></addElements>

    <addAttribute tag="..." name="..." value="..."/>
    <removeAttribute tag="..." name="..."/>

    <loopElements tag="..."></loopElements>
</androidManifestUpdates>
```

`[PROJECT LOCATION]/Intermediate/Android/APK/AndroidManifest.xml`に`<uses-permission>`, `<uses-feature>`, `<uses-library>`を追加します。
また、任意の要素、属性を追加、削除することもできます。

### proguardAdditions

```xml
<proguardAdditions>
    <insert>...</insert>
</proguardAdditions>
```

タグ内で操作した内容が`[PROJECT LOCATION]/Intermediate/Android/APK/proguard-project.txt`に反映されます。

### prebuildCopies

```xml
<prebuildCopies>
    <copyFile src="..." dst="..."/>
    <deleteFiles filespec="..."/>
</prebuildCopies>
```

ndk-buildで必要なライブラリやファイルをビルド前にコピーするために使います。

### resourceCopies

```xml
<resourceCopies>
    <copyFile src="..." dst="..."/>
    <deleteFiles filespec="..."/>
</resourceCopies>
```

ndk-buildの後に必要なファイルをコピーするために使います。
JavaのビルドはAPK作成時なので、追加したいJavaのソースファイルはこれでコピーすればよいです。

### AARImports

```xml
<AARImports>
    <insert>...</insert>
</AARImports>
```

`aar-imports.txt`が`$(Output)`に読み込まれるので`<insert></insert>`などでAARを追加できます。

### GameActivity.java更新用のセクション

ビルドターゲットにAndroidを追加すると以下のJavaソースファイルがコピーされます。

* コピー元: `[ENGINE INSTALL LOCATION]\Engine\Build\Android\java\src\com\epicgames\ue4\GameActivity.java`
* コピー先: `[PROJECT LOCATION]\Intermediate\Android\APK\src\com\epicgames\ue4\GameActivity.java`

このファイルはAPK作成時に独自のコードを追加するためのプレースホルダーがいくつかあり、以下のタグで操作することができます。

**注意**
`GameActivity.java`が作られるとXMLのJavaコードを変更しても反映されません。
コピー先の方の`GameActivity.java`を削除すると、次のAPK作成時に変更が反映されます。

#### gameActivityImportAdditions

```xml
<gameActivityImportAdditions>
    <insert>...</insert>
</gameActivityImportAdditions>
```

GameActivityにimportを追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityImportAdditions}$$`です。

#### gameActivityClassAdditions

```xml
<gameActivityClassAdditions>
    <insert>...</insert>
</gameActivityClassAdditions>
```

GameActivityにメンバ変数やメンバメソッドを追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityClassAdditions}$$`です。

#### gameActivityOnStartAdditions

```xml
<gameActivityOnStartAdditions>
    <insert>...</insert>
</gameActivityOnStartAdditions>
```

GameActivityの`onStart()`に処理を追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityOnStartAdditions}$$`です。

#### gameActivityReadMetadataAdditions

```xml
<gameActivityReadMetadataAdditions>
    <insert>...</insert>
</gameActivityReadMetadataAdditions>
```

GameActivityの`onCreate()`でAndroidManifest.xmlから設定を読み込むときに使います。
コピー元のプレースホルダーは`$${gameActivityReadMetadataAdditions}$$`です。

#### gameActivityOnCreateAdditions

```xml
<gameActivityOnCreateAdditions>
    <insert>...</insert>
</gameActivityOnCreateAdditions>
```

GameActivityの`onCreate()`に処理を追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityOnCreateAdditions}$$`です。

#### gameActivityOnDestroyAdditions

```xml
<gameActivityOnDestroyAdditions>
    <insert>...</insert>
</gameActivityOnDestroyAdditions>
```

GameActivityの`onDestroy()`に処理を追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityOnDestroyAdditions}$$`です。

#### gameActivityOnStopAdditions

```xml
<gameActivityOnStopAdditions>
    <insert>...</insert>
</gameActivityOnStopAdditions>
```

GameActivityの`onStop()`に処理を追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityOnStopAdditions}$$`です。

#### gameActivityOnPauseAdditions

```xml
<gameActivityOnPauseAdditions>
    <insert>...</insert>
</gameActivityOnPauseAdditions>
```

GameActivityの`onPause()`に処理を追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityOnPauseAdditions}$$`です。

#### gameActivityOnResumeAdditions

```xml
<gameActivityOnResumeAdditions>
    <insert>...</insert>
</gameActivityOnResumeAdditions>
```

GameActivityの`onResume()`に処理を追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityOnResumeAdditions}$$`です。

#### gameActivityOnActivityResultAdditions

```xml
<gameActivityOnActivityResultAdditions>
    <insert>...</insert>
</gameActivityOnActivityResultAdditions>
```

GameActivityの`onActivityResult()`に処理を追加するときに使います。
コピー元のプレースホルダーは`$${gameActivityOnActicityResultAdditions}$$`です。

#### soLoadLibrary

```xml
<soLoadLibrary>
    <loadLibrary name="..." failmsg="..."/>
    <insert>...</insert>
</soLoadLibrary>
```

GameActivityでロードしたいネイティブライブラリを追加するときに使います。
細かい処理をしたい場合は`<insert></insert>`を使うこともできます。
コピー元のプレースホルダーは`$${soLoadLibrary}$$`です。

## iOS用のセクション

以下のセクションは`ReceiptProperty()`で`IOSPlugin`を指定すると適用されます。

#### iosPListUpdates

```xml
<iosPListUpdates></iosPListUpdates>
```
このセクションの内容を`[PROJECT LOCATION]/Intermediate/IOS/[GAME NAME]-Info.plist`に書き出します。

## 各種の操作

### 変数定義

```xml
<setBool result="..." value="..."/>
<setInt result="..." value="..."/>
<setString result="..." value="..."/>
<setElement result="..." value="..."/>
<setElement result="..." xml="..."/>
```

`value`の値を`result`で指定した変数に代入します。

---

```xml
<setBoolFromProperty result="..." ini="..." section="..." property="..." default="..."/>
<setIntFromProperty result="..." ini="..." section="..." property="..." default="..."/>
<setStringFromProperty result="..." ini="..." section="..." property="..." default="..."/>
```

iniファイルから値を読み込みます。
ファイルやセクション、プロパティが存在しない場合は`default`が代入されます。

### 演算

```xml
<setBoolNot result="..." source="..."/>
<setBoolAnd result="..." arg1="..." arg2="..."/>
<setBoolOr result="..." arg1="..." arg2="..."/>
<setBoolIsEqual result="..." arg1="..." arg2="..."/>
<setBoolIsLess result="..." arg1="..." arg2="..."/>
<setBoolIsLessEqual result="..." arg1="..." arg2="..."/>
<setBoolIsGreater result="..." arg1="..." arg2="..."/>
<setBoolIsGreaterEqual result="..." arg1="..." arg2="..."/>
```

論理演算した結果を`result`で指定した変数に代入します。

---

```xml
<setIntAdd result="..." arg1="..." arg2="..."/>
<setIntSubtract result="..." arg1="..." arg2="..."/>
<setIntMultiply result="..." arg1="..." arg2="..."/>
<setIntDivide result="..." arg1="..." arg2="..."/>
```

算術演算した結果を`result`で指定した変数に代入します。

---

```xml
<setStringAdd result="..." arg1="..." arg2="..."/>
<setStringSubstring result="..." source="..." start="..." length="..."/>
<setStringReplace result="..." source="..." find="..." with="..."/>
```

文字列操作した結果を`result`で指定した変数に代入します。

---

```xml
<setIntLength result="..." source="..."/>
```

`source`の文字列の長さを`result`で指定した変数に代入します。

---

```xml
<setIntFindString result="..." source="..." find="..."/>
```

`source`から`find`の文字列が一致したインデックスを`result`で指定した変数に代入します。

---

```xml
<setBoolStartsWith result="..." source="..." find="..."/>
<setBoolEndsWith result="..." source="..." find="..."/>
<setBoolContains result="..." source="..." find="..."/>
```

それぞれ`find`で指定した文字列で始まる、終わる、含んでいるかチェックした結果を`result`で指定した変数に代入します。

### Output変数の操作

```xml
<replace find="..." with="..."/>
```

変数`$(Output)`から`find`と一致した文字列を`with`の文字列で置換します。

---

```xml
<insert>...</insert>
<insertValue value="..."/>
<insertNewLine/>
```

変数`$(Output)`にタグ内の文字を追加します。
主にproguardAdditionsやGameActivity.javaのセクションで使います。

### 制御構文

```xml
<if condition="...">
	<true>
		<!-- executes if boolean variable in condition is true -->
	</true>
	<false>
		<!-- executes if boolean variable in condition is false -->
	</false>
</if>
```

`condition`で条件を指定し、`true`タグと`false`タグでそれぞれの処理を設定できます。

---

```xml
<isArch arch="armeabi-armv7">
	<!-- do stuff -->
</isArch>
```

アーキテクチャをチェックできるショートカットノードです。これは以下と同等の操作ができます。

```xml
<setBoolEquals result="temp" arg1="$S(Architecture)" arg2="armeabi-armv7">
<if condition="temp">
	<true>
		<!-- do stuff -->
	</true>
</if>
```

---

```xml
<isDistribution>
	<!-- do stuff -->
</isDistribution>
```

Distributionビルドかどうかチェックできます。
これも`isArch`と同様にショートカットです。

---

```xml
<return/>
```

ここで処理を終了します。

---

```xml
<while condition="...">
	<!-- do stuff -->
</while>

<break/>
<continue/>
```

ループ処理です。`break`と`continue`もあります。

### XML操作

```xml
<addElement tag="..." name="..."/>
<addElements tag="..."> body </addElements>
<removeElement tag="..."/>
<setStringFromTag result="..." tag="..." name="..."/>
<setStringFromAttribute result="..." tag="..." name="..."/>
<addAttribute tag="..." name="..." value="..."/>
<removeAttribute tag="..." name="..."/>
<loopElements tag="..."> instructions </loopElements>
```

要素の追加および削除、属性の追加および削除、要素や属性の値を取得、繰り返し要素に対しての操作をできます。
主にAndroidManifest.xmlのintent-filterのように特定の操作が用意されていない要素を設定するために使います。

# まとめ

全体を見ると結構な量がありますが、柔軟な定義をするためだと思います。
たとえばAndroid側にロジックを追加したいときはGameActivityのセクションしか使いません。
また、プラグインでなくてもプロジェクト単体でも利用できるので使える範囲は広いです。