`Unity as a library` を試してみる

# 元ネタ

* [Using Unity as a library in native iOS/Android apps](https://forum.unity.com/threads/using-unity-as-a-library-in-native-ios-android-apps.685195/)
* [Integration Unity as a library in native iOS app](https://forum.unity.com/threads/integration-unity-as-a-library-in-native-ios-app.685219/)
* [Integration Unity as a library in native Android app](https://forum.unity.com/threads/integration-unity-as-a-library-in-native-android-app.685240/)

# `Unity as a library` って？ :thinking:

Unity2019.3.a2 以降では、Unityをネイティブアプリのライブラリとして使用できるようになるようです。
アプリ起動時にいきなりUnityが起動するのではなく、ネイティブのUIから動的にUnityが起動できるようになる感じです。

> Starting with Unity 2019.3.a2, we are introducing a new feature to use Unity as a library in native apps by integrating the Unity runtime components and your content in a native platform project. The Unity Runtime Library exposes controls to manage when and how to load/activate/unload within the native application.


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/5594cc20-91af-c2c3-d8c9-5bb93501cf6b.png)

これは上記のForumにあるサンプルアプリの画像ですが、アプリ起動時は左のViewが表示された状態です。
これはまだUnityは起動していない状態で、ボタンは全てネイティブUIです。
「Init」後に「Show Unity」ボタンを押すと右側のViewが開くのですが、こちらはUnityのViewとなっています。（色のボタンはネイティブUI）


# サンプルを試してみる（iOS）

サンプルアプリが用意されているので試してみます。
[手順はここに書いてある通りです](https://forum.unity.com/threads/integration-unity-as-a-library-in-native-ios-app.685219/)

以下の2つのプロジェクトを使用するため、ダウンロードしておきます。

* [UnityProject.zip](https://drive.google.com/file/d/1q8zyS6W5T6-fsWDiSznz_a7pPKsEcScj/view) 
* [NativeiOSApp.zip](https://drive.google.com/file/d/1XvYAyjVgBQSaQ24n-4NC8E3tg6_mjjvj/view)


## 1. UnityProjectのビルド

UnityProject.zip をダウンロードして展開したら、Unity2019.3.a2 以降で開きます。
開いたら早速TargetPlatformをiOSにしてビルドします。特別なことは不要で、いつも通りビルドするだけでOKです。


## 2. `Unity-iPhone.xcodeproj` を開く

いつも通りXcodeのProjectを開きます。
Xcodeの左上の「Unity-iPhone」をクリックして開くと、Build Targetに「UnityFramework」が選べるようになっています。
これを選択することで、`UnityFramework.framework` をビルドすることができます。

<img width="193" alt="スクリーンショット 2019-08-05 18.11.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/64b4ebaa-a3e3-c56a-0f4d-e91bacb46e4c.png">

さて、.framework をビルドする前にいくつか準備が必要です。

### 2-1. `NativeCallProxy.h`を.frameworkのPublic Headerにする

`Libraries/Plugins/iOS/NativeCallProxy.h` を選択し、`UnityFramework` にチェックを入れます。
その後、ドロップダウンで `Public` を選択します。

<img width="600" alt="スクリーンショット 2019-08-05 18.29.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/7535259a-5f98-6f5d-0e18-1586c857a3a9.png">


### 2-2. `Data` ディレクトリをframeworkに含める

`Data`ディレクトリを選択し、先ほどと同様`UnityFramework` にチェックを入れます。

<img width="600" alt="スクリーンショット 2019-08-05 18.35.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/b6f5b273-1e39-9317-b2d9-1a61f166e306.png">


### 2-3. `DisplayManager.mm` を修正

このままだとアプリ実行時にクラッシュするため、`DisplayManager.mm`の処理を修正します。
`recreateSurface` メソッドの最初の方にある、

```objective_c
if (UnitySelectedRenderingAPI() == apiMetal)
```

という部分を、以下のように変更します。


```objective_c
if (UnitySelectedRenderingAPI() == apiMetal && params.renderW != 0 && params.renderH != 0)
```

参考 : https://forum.unity.com/threads/integration-unity-as-a-library-in-native-ios-app.685219/#post-4709345

## 3. `UnityFramework.framework` をビルド

build targetに `UnityFramework` を選択したらビルドします。するとProducts配下に `UnityFramework.framework` が生成されます。

<img width="274" alt="スクリーンショット 2019-08-05 18.44.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/0f56837b-8c87-ccfb-59fa-4b5699274233.png">

`UnityFramework.framework` をビルドしたら、Xcodeプロジェクトを閉じます。


## 4. NativeiOSApp を開く

今度は `NativeiOSApp.zip` を展開した中の、`NativeiOSApp.xcodeproj` を開きます。
これは先ほどの `UnityFramework.framework` を使う側のプロジェクトになります。

### 4-1. `UnityFramework.framework` を追加する

先ほどビルドした `UnityFramework.framework` をプロジェクトに追加します。

<img width="277" alt="スクリーンショット 2019-08-05 18.53.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/64805758-9eec-fd9a-9600-385d58bfeff0.png">


追加したら設定を変更します。`General` タブの中の、「Embedded Binaries」に、`UnityFramework.framework`を追加し、
元々入っていた `Linked Frameworks and Libraries` からは `UnityFramework.framework` を削除します。

<img width="962" alt="スクリーンショット 2019-08-05 18.55.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/b7276177-6140-aebe-9ea9-4291e92494c3.png">

この.frameworkは実行時に動的に読み込むような処理になっており、そのためこのような設定になります。


### 5. ビルドして実行

これで準備ができたので、ビルドして実行してみます。
起動すると以下の左側の画面になります。

<img width="300" alt="IOS01.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/0ea81c26-0ce4-8a26-8b46-0008d46f6871.png">  <img width="300" alt="IOS02.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/cd0b4afb-3c30-8aaf-435a-13ac63ab778c.png">

緑の「Init」ボタンを押すと、Unityの初期化処理が走った後、右のUnityの画面が表示されます。
右画面の「Show Main」と、左画面の「Show Unity」で画面を切り替えることができ、
この場合はUnityはロードされたままの状態のため、スムーズに切り替わります。
「Unload」でUnityのインスタンスが破棄され、再度Initが必要になります。

## 補足

Forumの手順では `xcworkspace` を作り、2つの `xcodeproj` をまとめています。
実際に開発をする際はこの方がやりやすいと思います。（余計な説明が増えるのでこの記事では省きました）

<img width="210" alt="スクリーンショット 2019-08-05 22.02.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/f3972ee3-5726-aee7-6567-0327a5806112.png">



# サンプルを試してみる（Android）

iOSと同様、Androidの方もサンプルアプリを試してみます。

[Androidの手順はここです](https://forum.unity.com/threads/integration-unity-as-a-library-in-native-android-app.685240/)

Androidは以下の2つのプロジェクトをダウンロードしておきます。
`UnityProject` はiOSと同じものです。

* [UnityProject.zip](https://drive.google.com/file/d/1q8zyS6W5T6-fsWDiSznz_a7pPKsEcScj/view) 
* [NativeAndroidApp.zip](https://drive.google.com/file/d/1f6Hv7Ir3qzgAG3xwxfFZysK7ppWVK4aV/view)


## 1. UnityProjectのビルド

iOSと同様、Unity2019.3.a2 以降で開きます。
TargetPlatformをAndroidにしたら、BuildではなくExportします。

Exportすると以下のような構成になっています。
AndroidStudioのModuleが、`unityLibrary`と`launcher`に分かれるようになったようです。

<img width="164" alt="スクリーンショット 2019-08-05 22.09.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/7a85cb58-5896-b215-f7e1-a3ce9b64ec36.png">

この `unityLibrary` だけを別のプロジェクトから使用する形になります。


## 2. NativeAndroidApp を開く

Android Studioで、zipを展開した `NativeAndroidApp` を開きます。
手順通りに設定を書き換えていきます。


### 2-1. `settings.gradle` に追記

以下を追記し、先ほどExportした `unityLibrary` をincludeします。パスは適宜変更してください。

```gradle
include ':unityLibrary'
project(':unityLibrary').projectDir=new File('../UnityProject/androidBuild/unityLibrary')
```


### 2-2. `app/build.gradle` に追記

`app`モジュールの`build.gradle`を開き、dependenciesに`unityLibrary`を追記します。

```gradle
dependencies {
    implementation project(':unityLibrary')
    ...
}
```

また、`minSdkVersion`が19未満だとsync時にエラーが出るので、19未満の場合は19以上に変更します。

```gradle
    defaultConfig {
        ...
        minSdkVersion 19
        ...
    }
```



### 2-3. `Project/build.gradle` に追記

プロジェクト直下の方の`build.gradle`を開き、`repositories`の中に以下の`flatDir {...}`を追記します。

```gradle
allprojects {
    buildscript {
        repositories {
            google()
            jcenter()
            flatDir {
                dirs "${project(':unityLibrary').projectDir}/libs"
            }
        }
        ...
    }
    ...
}
```


## 3. syncして実行する

Androidはこれで準備完了です。あとはsyncしてからビルドして実行します。
iOSと同じような画面が開きます。

<img width="300" alt="Android01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/37a6f696-aefc-84ce-f531-0d1d299c293a.png">  <img width="300" alt="Android02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19872/8cd77f16-51a8-d510-ad9f-a4394724735c.png">