Help us understand the problem. What is going on with this article?

Unity + DockerでUnityプロジェクトからAARファイルを自動生成

はじめに

UnityのSceneをAndroidのSubView(FrameLayout)として埋め込むまで
https://qiita.com/_nonono/items/253aa15d6027ecc8ad66
このような記事があり、AndroidにUnityのシーンをFrameLayoutに埋め込むことができます(便利

Unityで開発せずビルド成果物(AARファイル)のみ必要な場合
1.指定のバージョンのUnityをインストール
2.Unityを立ち上げAndroidプロジェクトをExport
3.Android Studioでビルド
上の3つの作業を手作業で行うので一苦労だと思います
Unity、Android Studioのバージョンを意識したりしなければならいので良い方法が必要です

「なんとか良い方法を!」と思って Unityの入ったDockerイメージを使ってAARの生成を自動化 してみたので、作業を下に記していきます

TL;DR

いきなり一連の作業をスクリプトにできないので、dockerの中で作業を1つずつして、最後にスクリプトにまとめます

やったこと

  1. docker pull gableroux/unity3d
  2. Dockerコンテナに入りライセンスファイルを用意
  3. ライセンスファイルをコピー
  4. ビルド用クラスを作成
  5. Unityプロジェクトをビルド
  6. build.gradleとAndroidManifest.xmlを編集
  7. GradleでAndroidプロジェクトをビルド
  8. build.shを書いて完全自動化

実際の環境

・MacBook Pro (15-inch, 2016) macOS 10.13.6(17G10007)
・docker desktop ver. 2.1.0.5(40693)

1. Unityの入ったDockerイメージをpullする

まずは、Unityの入っているDockerイメージをインターネットから落としてきます

次のコマンドを実行すると最新版のコンテナが自分のローカルに保存されます

Dockerイメージをpull
docker pull gableroux/unity3d

しかし、このイメージではAndroidプロジェクトを生成/ビルドができないので、UnityバージョンとUnityプロジェクトのビルドターゲットを指定しなければなりません。

この記事では、 Unity 2018.4.5f1 でビルドターゲットをAndroid とするので、Dockerイメージのtagに2018.4.5f1-androidを指定します
※目的のバージョンのイメージが無い場合があるので、 DockerHub のページで確認しておきましょう

目的のDockerイメージをpull
docker pull gableroux/unity3d:2018.4.5f1-android

docker imagesを実行するとpullしてきたイメージが確認できます

DockerImageの確認
cha84rakanal$ docker images
REPOSITORY            TAG                  IMAGE ID            CREATED             SIZE
gableroux/unity3d     2018.4.5f1-android   3cc6afa5c2bd        3 months ago        6.7GB

docker run -it {image name} bash を実行すればイメージからコンテナが作成され、Dokcer内のターミナルに接続できる
なので、接続してUnityAndroid SDKがあるかを確認しておきます

UnityとAndroidSDKの確認
cha84rakanal$ docker run -it --rm gableroux/unity3d:2018.4.5f1-android bash
root@7462f939f4d0:/# echo $ANDROID_HOME
/opt/android-sdk-linux
root@7462f939f4d0:/# ls /opt/
android-sdk-linux  Unity  Unity-2018.4.5f1
root@7462f939f4d0:/# 

2. Dockerコンテナに入ったUnity用のライセンスを生成する(1回のみ・手作業)

普段使うmacOSやWindowsでUnityを一番最初に起動すると次の画面が出てアクティベーションをする必要があります
アクティベーション画面

次の作業は、DockerコンテナにインストールされているUnityのアクティベーションを行います

まず、Unityプロジェクトジェクトのディレクトリに移動してdockerコンテナを立ち上げます
パスワードを平打ちするので気をつけましょう

Dockerコンテナの立ち上げ
cd /path/to/project
docker run -it --rm \
-e "UNITY_USERNAME=username@example.com" \
-e "UNITY_PASSWORD=example_password" \
-e "TEST_PLATFORM=linux" \
-e "WORKDIR=/root/project" \
-v "$(pwd):/root/project" \
gableroux/unity3d:gableroux/unity3d:2018.4.5f1-android \
bash

次に、Dockerコンテナ内で以下のコマンドを実行します

アクティベーションをする
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
/opt/Unity/Editor/Unity \
-logFile /dev/stdout \
-batchmode \
-username "$UNITY_USERNAME" -password "$UNITY_PASSWORD"

すると、出力に次のようなXMLが表示されるので、表示されたXMLを unity3d.alf というファイル名で保存します

ターミナルでの出力
LICENSE SYSTEM [2017723 8:6:38] Posting <?xml version="1.0" encoding="UTF-8"?><root><SystemInfo><IsoCode>en</IsoCode><UserName>[...]

もし、XMLではなく401エラーが出た場合は、2段階認証を切りましょう
GoogleでサインインFaceBookでサインイン でUnity IDを作成している場合でも 401エラーがでるので、メールアドレスから作成したUnityIDを使いましょう

401エラー
Can't activate unity: No sufficient permissions while processing request HTTP error code 401

unity3d.alf が用意できたら https://license.unity3d.com/manual にアクセスします。アクセスしたらサイトの指示に従ってunity3d.alfをアップロード、質問に答えていきましょう。すべて終わるとUnity_v2018.x.ulfUnity_v2019.x.ulfがダウンロードできるので保存しておきます。
このUnity_v*.x.ulfファイルは今後必要になるので大切に保管しておきます

プロジェクトチームの誰かがこの作業をやれば、Unityのバージョンを変えない限り、
3以降の作業でUnity_v2018.x.ulfを使いまわすことができます(多分

※アクティベーションの詳しい手順は このページ に書いてありますが、まとめとしてこの記事にも記しています

3. Dockerコンテナに入ったUnityにライセンスを当てる

2の手順でダウンロードしてきたファイルUnity_v2018.x.ulfをUnityプロジェクトのルートディレクトにおきます

Unity_v2018.x.ulfの設置
/path/to/project
├── Assets
├── Library
├── Logs
├── Packages
├── ProjectSettings
├── README.md
└── Unity_v2018.x.ulf

次に、Unityプロジェクトジェクトのディレクトリに移動してdockerコンテナを立ち上げます

Dockerコンテナの立ち上げ
cd /path/to/project
docker run -it --rm \
-v "$(pwd):/root/project" \
gableroux/unity3d:gableroux/unity3d:2018.4.5f1-android \
bash

ここから先の作業7.が終わるまでは、dockerコンテナ内のターミナルでexitをしてはいけません

ライセンスファイルをUnityプロジェクトのルートディレクトに設置できたら、dockerコンテナ内のターミナルで次のコマンドを実行します。これでライセンスファイルの設置は完了です

Unityのライセンスファイルを設置
set -e
set -x
mkdir -p /root/.cache/unity3d
mkdir -p /root/.local/share/unity3d/Unity/
set +x
cp ~/project/Unity_v2018.x.ulf /root/.local/share/unity3d/Unity/Unity_lic.ulf

4. ビルド用クラスを作成してAssets/Scripts/Editorに配置

Unityプロジェクトをコマンドラインからビルドできるように次のC#ソースをAssets/Scripts/Editorに設置します

ApplicationBuild.cs
using UnityEngine;
using System;
using System.Linq;

public static class ApplicationBuild {

    private static string[] GetAllScenePaths() {
        return EditorBuildSettings.scenes
            .Where(scene => scene.enabled)
            .Select(scene => scene.path)
            .ToArray();
    }

    public static void AndroidBuild() {

        string[] scenes = GetAllScenePaths();
        BuildPipeline.BuildPlayer(scenes, "./Build/", BuildTarget.Android, BuildOptions.AcceptExternalModificationsToPlayer);

    }

}

公式ドキュメント を確認すると、 ビルドのパラメータを色々設定できます。
今回は、プロジェクトのエクポートパスは./Build 、ビルドターゲットはAndroidなので BuildTarget.Android 、AndroidのビルドはExternalで行うのでオプションに BuildOptions.AcceptExternalModificationsToPlayer を指定しています

BuildPipeline.BuildPlayer
BuildPlayer(EditorBuildSettingsScene[] levels, string locationPathName, BuildTarget target, BuildOptions options)

パラメータ
levels The Scenes to be included in the build. If empty, the currently open Scene will be built. Paths are relative to the project folder (Assets/MyLevels/MyScene.unity).
locationPathName 成果物の保存先のパス
target ビルドする BuildTarget
options ビルドしたプレイヤーを実行するか、などの追加の BuildOptions

5. コマンドラインからUnityプロジェクトをビルド

次のコマンドでUnityプロジェクトをコマンドラインからビルドできます

コマンドラインからUnityプロジェクトをビルド
/opt/Unity/Editor/Unity -batchmode -quit -nographics -logFile ./build.log -projectPath . -executeMethod ApplicationBuild.AndroidBuild

ログは ./build.log に保存されるので、エディタ等で確認するとビルドが進んでるのがわかります
ビルド中は、ターミナルに何かこれといって表示されるものは無いです(無視できるエラーとかは出る

ビルドが完了するとログに次の行が記録されます

build.log
Exiting batchmode successfully now!

※成果物の保存先のパス ./Buildディレクトリがすでにあるとビルドが停止してしまうので消しておきましょう

6. APKではなくAARを作成するようにbuild.gradleとAndroidManifest.xmlを編集

5の作業が終わり、ビルドが成功すると、./BuildディレクトリにAndroidプロジェクトが生成されます

コマンドラインでのビルド後
/path/to/project
├── Assets
├── Build
│   └── {Project Name}
│       ├── build.gradle
│       ├── gradle.properties
│       ├── libs
│       ├── local.properties
│       ├── proguard-unity.txt
│       └── src
├── Library
├── Logs
├── Packages
├── ProjectSettings
├── README.md
└── Unity_v2018.x.ulf

デバイスにインストールする.apkの作成であれば、gradle コマンドでビルドして終了です
今回は、.aarの作成なので、build.gradleAndroidManifest.xmlを編集する必要があります

build.gradle

  • apply plugin: 'com.android.application'apply plugin: 'com.android.library' に変更
  • applicationId 'com.project.unitytest' を削除
  • bundle をコメントアウト(Unity2018.3.x以降??)
build.gradle
--- apply plugin: 'com.android.application'
+++ apply plugin: 'com.android.library'

--- applicationId 'com.project.unitytest'  

--- bundle {
+++ /*bundle {
        language {
            enableSplit = false
        }
        density {
            enableSplit = false
        }
        abi {
            enableSplit = true
        }
--- }
+++ }*/

AndroidManifest.xml

  • 該当のintent-filterタグとその子をすべてコメントアウト
AndroidManifest.xml
<!--<intent-filter>-->
<!--<action android:name="android.intent.action.MAIN" />-->
<!--<category android:name="android.intent.category.LAUNCHER" />-->
<!--<category android:name="android.intent.category.LEANBACK_LAUNCHER" />-->
<!--</intent-filter>-->

UnityプロジェクトからAARファイルの生成を行うには、この作業も自動化しておく必要があるので、
UnityのPostProcessの機構を使って自動化しておきます

[Unity] PostProcessでビルド後に処理を差し込む
https://qiita.com/edo_m18/items/346439f7678218e85e69

build.gradleの文字コードは UTF-8 で書き出すこと、 C#のでのUTF8EncodingUTF-8 with BOMであるので注意が必要。 UTF-8 with BOMbuild.gradlegradleに渡すとエラーになるので注意
※UnityのPostProcessBuildにはこの記事ではふれません
build.gradlebundleのコメントアウトだが このページ によれば次に示す置換でも問題ない

build.gradle
--- bundle {
+++ splits {
        language {
---         enableSplit = false
+++         enable false
        }
        density {
---         enableSplit = false
+++         enable false
        }
        abi {
---         enableSplit = true
+++         enable true
        }
    }
bundle部分を処理するC#の例
build_text = build_text.Replace("bundle {", "splits {");
build_text = build_text.Replace("enableSplit = false", "enable false");
build_text = build_text.Replace("enableSplit = true", "enable true");

7. コマンドラインでAndroidプロジェクトをビルド

残る作業は./Buildディレクトリに生成されたAndroidプロジェクトをビルドしてAARファイルを生成するだけです
生成されたAndroidプロジェクトのディレクトリ(build.gradleがあるディレクトリ)に移動して次のコマンドを実行するだけです

AARファイルを生成
gradle bundleDebugAar
AARファイルを生成
gradle bundleReleaseAar

デバッグとリリースがあるので必要に応じて使い分けましょう

ビルドが成功するとプロジェクトのディレクトリにbuildディレクトリが生成されて、
その中に {PROJECT_NAME}-debug.aar {PROJECT_NAME}-release.aar が生成されます

ビルド後
/path/to/project
├── Assets
├── Build
│   └── {Project Name}
│       ├── build
│       │   └── outputs
│       │       └── aar
│       │           ├── {PROJECT_NAME}-debug.aar
│       │           └── {PROJECT_NAME}-release.aar
│       ├── build.gradle
│       ├── gradle.properties
│       ├── libs
│       ├── local.properties
│       ├── proguard-unity.txt
│       └── src
├── Library
├── Logs
├── Packages
├── ProjectSettings
├── README.md
└── Unity_v2018.x.ulf

8. 作業をシェルスクリプトにまとめる

4から7の作業をまとめて、コマンド一行でAARファイルの生成できるようにします
ライセンスファイルはUnityプロジェクトのルートディレクトリに設置して、6の作業はUnityのPostProcessBuildで自動化しておきましょう

そうして作業をまとめたスクリプトが次になります

build.sh
#!/usr/bin/env bash

set -e
set -x
mkdir -p /root/.cache/unity3d
mkdir -p /root/.local/share/unity3d/Unity/
set +x

cp ~/project/Unity_v2018.x.ulf /root/.local/share/unity3d/Unity/Unity_lic.ulf

cd ~/project/ && rm -r ./Build && /opt/Unity/Editor/Unity -batchmode -quit -nographics -logFile ./build.log -projectPath . -executeMethod ApplicationBuild.AndroidBuild
cd ~/project/Build/{Project Name} && gradle bundleDebugAar

Unityプロジェクトのルートディレクトリに移動して、dockerコンテナ内でbuild.shを実行します

AARの自動ビルド
cd /path/to/project
chmod 777 build.sh
docker run -it --rm \
-v "$(pwd):/root/project" \
gableroux/unity3d:gableroux/unity3d:2018.4.5f1-android \
/bin/bash -c "/root/project/build.sh"

さいごに

これでコマンドを実行してビルドされるのを待つだけになりました!
これをBitbucket PipelineCircleCIGitHub Actions とかのCIツールに載せていけるといいですね
まとめている間に、既にmacOSやWindowsにUnityがインストールされていて、かつ、Android SDKがインストールされているなら、Docker上じゃなくていいかなと思いました

余談

実際の出力
cha84rakanal$ time docker run -it --rm -e "WORKDIR=/root/project" -v "$(pwd):/root/project" gableroux/unity3d:2018.4.5f1-android /bin/bash -c "/root/project/build.sh"
+ mkdir -p /root/.cache/unity3d
+ mkdir -p /root/.local/share/unity3d/Unity/
+ set +x
ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5007:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM default
ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5007:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM default
/home/builduser/buildslave/unity/build/Editor/Platform/Linux/UsbDevices.cpp:UsbDevicesQuery

Welcome to Gradle 5.1.1!

Here are the highlights of this release:
 - Control which dependencies can be retrieved from which repositories
 - Production-ready configuration avoidance APIs

For more details see https://docs.gradle.org/5.1.1/release-notes.html

Starting a Gradle Daemon (subsequent builds will be faster)

> Task :help

Welcome to Gradle 5.1.1.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

To see more detail about a task, run gradle help --task <task>

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 5s
1 actionable task: 1 executed
debugger-agent: Unable to listen on 28
Starting a Gradle Daemon (subsequent builds will be faster)

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.5.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 2m 7s
21 actionable tasks: 21 executed

real    33m9.065s
user    0m0.162s
sys     0m0.178s
cha84rakanal$ 

timeコマンドで実行時間を図ったら30分もビルドしてるので、dockerコンテナ内よりホスト側でビルドした方がはやいやんけw

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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした