Unity2018.1からのBuildPlayerを試す

Unity 2018.1より、BuildPipeline.BuildPlayerのAPIが更新されました。

この記事ではその変更内容と、実際に導入してみて少しハマった事例について紹介したいと思います。

APIの変更点

Unity 2017.xまでは、BuildPipeline.BuildPlayerの戻り値はstringでした。

この戻り値はそのビルドでエラーが発生した際に、そのエラー内容を 文字列 で返却します。

Unity2018.1からはこの戻り値がstringではなく、Build.Reporting.BuildReportを返却するようになりました。

Build.Reporting.BuildReportとは

Build.Reporting.BuildReportは文字通り、Unityのビルドプロセスの結果を保持するクラスです。

結果クラスを返してくれることで、ビルド時にどういう過程がありどの程度時間がかかったのか、どういう箇所でエラーが発生したのかなど、ビルドの詳細を把握しやすくなりました。

このクラスの保持するデータについて、説明していきます。

BuildReport.summary

文字通り、ビルド結果の概要を保持するクラスになります。 よく使いそうな変数を列挙します。

変数名 説明
result ビルド結果をBuildResultで保持します
totalErrors ビルドの総エラー数を保持します
totalWarnings ビルドの総警告数を保持します
totalSize ビルド成果物のサイズをbytesで保持します
totalTime ビルドの総時間をSystem.TimeSpanで保持します

ビルドそのものが成功したかどうかは、result変数を介して取得することができます。

後述しますが、ビルドが失敗したときには、いままでは例外を投げていたのですが、2018.1からは例外を投げなくなったので、result変数をみてエラーハンドリングをする必要があります。

また、警告やエラー数・ビルド成果物のサイズを取得できるようになったため、Jenkinsなどでビルドしたときのビルドの統計情報を作成が楽になりました。

BuildReport.steps

ビルドの各ステップの情報を配列で保持しています。

変数名 説明
name ビルドステップの名前を保持します
depth ビルドステップのネストの深さを保持します
duration そのステップに要した時間を秒単位で保持します
messages そのステップでのログの一覧を保持します。各メッセージは、ログの種類(Warning, Errorなど)と実際のメッセージを保持します

namedurationによって、各ビルドにはどういう工程が存在し、それらがどの程度時間がかかっているかがわかるようになったため、

例えば、ビルド時間の改善の手がかりなどにうまく利用できるのではないのかなと思います。

また、各ビルドステップごとにログを吐いてくれるのも嬉しところです。

ちなみに実験的ではありますが、ビルドステップに Compile scriptsという工程がありますが、プロジェクトで記述したスクリプトの警告ログもここに吐くようです。

こちらのログを収集して、例えばJenkinsでCIをしているなら通知することで、プロジェクトの品質維持に効果的かもしれません。

BuildReport.files

ビルドプロセスで生成されたファイル情報が配列で保持されています。

変数名 説明
path 生成されたファイルの絶対パスを保持します
role 生成されたファイルの役割を保持します
size ファイルサイズをbyte形式で保持します

実験的ではありますが、具体的に、空のサンプルプロジェクトのiOSビルドした際には、以下のような情報が取得できました。

BuildFile path: /path-to-project/Temp/StagingArea/Data/Managed/UnityEngine.UI.dll
BuildFile role: ManagedLibrary
BuildFile size: 250368
BuildFile path: /path-to-project/Temp/StagingArea/Data/Managed/UnityEngine.Networking.dll
BuildFile role: ManagedLibrary
BuildFile size: 255488
BuildFile path: /path-to-project/Temp/StagingArea/Data/Managed/UnityEngine.Timeline.dll
BuildFile role: ManagedLibrary
BuildFile size: 92672
BuildFile path: /path-to-project/Temp/StagingArea/Data/Managed/UnityEngine.SpatialTracking.dll
BuildFile role: ManagedLibrary
BuildFile size: 9216
BuildFile path: /path-to-project/Temp/StagingArea/Data/globalgamemanagers
BuildFile role: GlobalGameManagers
BuildFile size: 28776
BuildFile path: /path-to-project/Temp/StagingArea/Data/level0
BuildFile role: Scene
BuildFile size: 5281
BuildFile path: /path-to-project/Temp/StagingArea/Data/globalgamemanagers.assets
BuildFile role: SharedAssets
BuildFile size: 24908
BuildFile path: /path-to-project/Temp/StagingArea/Data/sharedassets0.assets
BuildFile role: SharedAssets
BuildFile size: 21160
BuildFile path: /path-to-project/Temp/StagingArea/Data/Resources/unity_builtin_extra
BuildFile role: BuiltInShaders
BuildFile size: 638968
BuildFile path: /path-to-project/Temp/StagingArea/Data/boot.config
BuildFile role: BootConfig
BuildFile size: 55
BuildFile path: /path-to-project/Temp/StagingArea/Data/unity default resources
BuildFile role: Unity default resources
BuildFile size: 6133660
BuildFile path: /path-to-project/Temp/StagingArea/Data/Managed/mono/2.0/machine.config
BuildFile role: Mono 2.0 machine.config
BuildFile size: 27625
BuildFile path: /path-to-project/Temp/StagingArea/Data/Managed/mono/4.0/machine.config
BuildFile role: Mono 4.0 machine.config
BuildFile size: 33648
BuildFile path: /path-to-project/iOSBuild/Libraries/libiPhone-lib.a
BuildFile role: Unity Player static library
BuildFile size: 539456888
BuildFile path: /path-to-project/iOSBuild/Libraries/libil2cpp.a
BuildFile role: IL2CPP static library
BuildFile size: 97469512

BuildReport.strippingInfo

このビルドに含まれているUnityEngineのネイティブコードモジュールの一覧と、その理由に関する情報を保持します。

この変数は、そのビルド対象のプラットフォームがcode strippingをサポートするプラットフォームであるときのみ利用できます。

サポートしない場合は、nullが代入されます。

含まれているネイティブコードモジュールの一覧を取得するためには、BuildReport.strippingInfo.includedModulesプロパティを参照します。

そのモジュールがなぜ含まれているのかを知るには、BuildReport.strippingInfo.GetReasonsForIncludingに、モジュール名を引数で渡すことで取得できます。

エラーハンドリングの変更点

いままでBuildPipeline.BuildPlayerは、ビルド中にエラーが発生すると、例外を投げる実装になっていましたが、

Unity 2018.1からは、ビルド成功有無にかかわらず、例外を投げないようになりました。

Unityをバッチモードで起動した場合の戻り値は、例外を投げた場合には1EditorApplication.Exitを実行した場合は、

そのExitの引数がそのまま戻り値に、それ以外の場合は0、つまり成功という扱いになります(参考: -executeMethod)

そのため例えば下記のように、単純にBuildPipeline.BuildPlayerを実行するだけでは、ビルドが失敗した場合でも成功扱いになってしまいます。

using UnityEditor;
using UnityEngine;

// 引用: https://docs.unity3d.com/2018.1/Documentation/ScriptReference/BuildPipeline.BuildPlayer.html
public class BuildPlayerExample : MonoBehaviour
{
    public static void MyBuild()
    {
        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
        buildPlayerOptions.scenes = new[] { "Assets/Scene1.unity", "Assets/Scene2.unity" };
        buildPlayerOptions.locationPathName = "iOSBuild";
        buildPlayerOptions.target = BuildTarget.iOS;
        buildPlayerOptions.options = BuildOptions.None;
        BuildPipeline.BuildPlayer(buildPlayerOptions);
    }
}
# 下記のコマンドが、戻り値0となる
# 例えばJenkinsビルドで、戻り値が非0で失敗するように組んでいると
# 失敗していようが成功扱いとしてしまう
/Path/To/Unity -batchmode -executeMethod BuildPlayerExample.MyBuild -buildTarget iOS

一方今回の変更で、ビルド情報がBuild.Reporting.BuildReportに含まれるようになりました。

適切にエラーハンドリングを行うためには、BuildReport.summary.resultを見てやる必要があります。

下記に実装例を記載します。

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
using System.Text;

// 引用: https://docs.unity3d.com/2018.1/Documentation/ScriptReference/BuildPipeline.BuildPlayer.html
public class Builder : MonoBehaviour
{
    public static void MyBuild()
    {
        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
        buildPlayerOptions.scenes = new[] { "Assets/Scenes/Scene1.unity" };
        buildPlayerOptions.locationPathName = "iOSBuild";
        buildPlayerOptions.target = BuildTarget.iOS;
        buildPlayerOptions.options = BuildOptions.None;

        BuildReport buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);

        // // buildReport.resultを見て、戻り値を決定する
        if (buildReport.summary.result == BuildResult.Succeeded)
        {
            const int kSuccessCode = 0;
            EditorApplication.Exit(kSuccessCode);
        }
        else
        {
            const int kErrorCode = 1;
            EditorApplication.Exit(kErrorCode);
        }
    }
}
#endif

まとめ

Unity 2018.1におけるBuildPipeline.BuildPlayerについての変更点と、変更に合わせてハマった点について紹介しました。

もともとのBuildPipeline.BuildPlayerは返す情報がstringだけだったので、おそらく多くの開発者がEditor.logなどを解析して、CIでの情報を充実させることが多かったと思うので、この変更は良いなと感じました。

 

UnityWebRequestによるHTTP通信

こんにちは。最近はクライアントエンジニアとしてUnityを書いている向井です。

今回は、Unity5.4のリリースで正式リリースされる予定のUnityWebRequestについて紹介したいと思います。
ほぼ公式ドキュメントからの抜粋という形になりますが、日本語による情報も少ないため、ご紹介したいと思います。

続きを読む “UnityWebRequestによるHTTP通信”

スプレッドシートによるAPIドキュメントの管理とクラスの自動生成

こんにちは。サーバーサイドエンジニアをしている向井です。

今回は新規プロジェクトで導入を検討している、スプレッドシートによるAPIドキュメントの管理方法と、ドキュメントからクラスの自動生成する方法について紹介します。

続きを読む “スプレッドシートによるAPIドキュメントの管理とクラスの自動生成”

総会AIバトルを支える技術

こんにちは。普段はサーバーサイドJavaを書いている新卒プログラマの向井です。
弊社の社員総会の企画の一つとして社内のプロジェクト対抗で「ゲームAIバトル」を開催しました。 今回の記事では「ゲームAIバトル」を開催する上で利用した技術について紹介したいと思います。
社内向けイベントの企画のため、興味を持っていたが活用する機会がなかったGolangやDockerを活用する良い機会となりました。

続きを読む “総会AIバトルを支える技術”

Unity & サーバー間通信でのMessagePack導入奮闘記(サーバー導入編)

こんにちは。新卒でサーバーサイドエンジニアをしている向井です。

今回からは、MessagePackをプロジェクトでの導入するにあたっての実用話や苦労話を、複数回に分けてお届けします。

本エントリでは、新規プロジェクトでMessagePackの導入に至った背景と、サーバーサイドでのMessagePackの導入について紹介します。

続きを読む “Unity & サーバー間通信でのMessagePack導入奮闘記(サーバー導入編)”