🤞

ProcessXでUnity埋め込みネイティブアプリのビルド手順を自動化させる

2022/01/26に公開

ProcessX は UniTaskMagicOnion の開発・メンテを行っている Cysharp の OSS のひとつです。

https://github.com/Cysharp/ProcessX

ProcessX simplifies call an external process with the aync streams in C# 8.0 without complex Process code. You can receive standard output results by await foreach, it is completely asynchronous and realtime.
(DeepL訳) ProcessXは、C# 8.0のayncストリームを使った外部プロセスの呼び出しを、複雑なプロセスコードなしで簡素化します。また、await foreachで標準出力の結果を受け取ることができ、完全に非同期でリアルタイムな処理が可能です。

これだけだとピンとこないかもしれませんが、こちらの記事によるとシェルスクリプト風にC#で記述することができるようです。
https://zenn.dev/okazuki/articles/zx-csharp-intro

筆者はとある事情でUnityアプリ単体では動作せず、WPFからUnityアプリのプロセスを起動させて動作するアプリケーションの開発をしています。そこで課題になったのが、実行するたびに

Unityで手動ビルド -> WPFで手動ビルド -> exeをたたく

みたいに起動して動作確認をしており、めんどくさい作業を強いられることでした。
シェルの達人ならサクッとやってしまうのでしょうが、筆者のシェルスキルはそこまででもなく、windowsにシェル系の実行環境を用意するのも腰が重かったのですが、上記の記事を見て ProcessX でビルドの自動化ができないかと試してみました。

結論としては非常に良く、筆者みたいにbashやzshの挙動や各種コマンドには詳しくないが、C#でのプログラム経験がある人にはおススメです!

以下で自分の使用例について書いていきます。

ビルドを自動化する

前述の通り、筆者のプロジェクトは WPFアプリのビルドと Unityのビルドから構成されます。
今回、双方同じディレクトリにビルドを出力し、wpfのexeを起動すると中でUnityが起動することを目標とします。

環境やディレクトリ構成は以下とします

環境
Unity: 2021.2.8
Visual Studio: 2022 (17.0.2)
Widnows10
ディレクトリ
root/
├ WpfProject/
├ UnityProject/
└ BuildOutput/

1. ProcessXの導入

新たにビルドプロセス用のプロジェクトを作成します。
VisualStudio からも作れるはずですが、筆者は VSCode の PowerShell のターミナルから作成しました。

(導入は上記記事に準じています)
BuildProcess はプロジェクト名なので任意の名前にしてください。

> dotnet new console -o BuildProcess
> cd BuildProcess
> dotnet add package ProcessX

これてプロジェクト作成と ProcessX 導入は終わりです。
あとは試しに、

Program.cs
using Zx;
using static Zx.Env;

var currentBranch = await "git branch --show-current";
log($"現在のブランチは {currentBranch} です!!");

として、実行すると

> dotnet run
main
現在のブランチは main です!!

となります。大変わかりやすい導入で感謝です。

2.WPFのPublish

Visual Studio からビルドするのでなく、今回はPublishコマンドでexeを生成しようと思います。
(なお、筆者の WpfProject は net6 環境です)

await ($"dotnet publish ../WpfProject " +
    $"/p:version={args[0]} " +
    $"/p:PublishDir={args[1]}");

ポイント1 改行する場合は()で囲う。

await "ながーいコマンド"; を途中で改行すると、await の対応範囲が変わってしまうのでコンパイルエラーになります。見やすくするために改行するときは () で囲ってやりましょう。

ポイント2 コマンドライン引数は args で参照する

Program.cs では自動で args という string[] が宣言されており、コマンドライン引数に参照できます。例えば上記の実行例は以下のようになります

> dotnet run -- 1.0.0 ../../BuildOutput

3.ファイルの操作

ここが嬉しいのですが、Program.cs 内はC#の世界なので、File.Exists()File.Delete() が使えます。また、ファイルの中身を書き換えたいときも File.ReadAllText() からの string.Replace() でごり押しできます。
ビルド前に、ビルド先の旧ファイルを削除したり念のためバックアップしたりもC#のコードで書けるというわけです。

4. Unityでのビルド

Unity は CLI からでもビルドやテストが実行できます。
使い方はこちらを参考にしました (こちらの記事はMacでの使い方になるようです)
https://zenn.dev/mattak/articles/5dfa1a2369ebc0

Unityのインストール場所を突き止める

まず、Unityのパスを知る必要があります。
Unity Hub から[インストール]でUnityの一覧を開き、歯車のマークから[エクスプローラで表示]を選びます。

するとエクスプローラが開くので Unity.exe を探し、Shiftキーを押しながら右クリックをして[パスをコピー]を選択します。

すると "C:\Program Files\Unity\Hub\Editor\2021.2.8f1\Editor\Unity.exe" のような文字列が得られます。

Unityのバージョンを見てみる

ビルドの説明の前に試しにバージョンを表示させてみます。

const string unityExe = "\"C:\\Program Files\\Unity\\Hub\\Editor\\2021.2.8f1\\Editor\\Unity.exe\"";
await $"{unityExe} -version";

実行結果は

> dotnet run
2021.2.8f1

のようになるはずです。
しれっと書いてますが、パスにスペース等が含まれている場合は文字列をさらに "" で囲う必要があり、C#では var path="\"スペースとか含むパス\""; のように書いてやる必要があるようです。

Unityプロジェクトのビルド

ビルドを行う上で注意点がいくつかあります。

  • Unityを起動している最中は失敗する
  • Unityにライセンスを読み込ませていない場合はCLIから読ませる必要がある

コマンドを実行する際はUnityのウィンドウは閉じておくほうがベターです。ライセンスはPCでUnityを起動したことがあれば問題ないと思われます。

Unityプロジェクトにビルドスクリプトを追加する

ビルドのコマンドを実行すればよいわけでなく、Unity内にビルドスクリプトを定義し、それを外から呼び出す形になります。

ビルドスクリプトの例は上記参考記事のものです。

namespace Sample
{
    public static class Builder
    {
        public static void Build()
        {
            var scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray();
            var options = new BuildPlayerOptions
            {
                target = BuildTarget.WebGL,
                targetGroup = BuildTargetGroup.WebGL,
		//locationPathName = "Build",
                locationPathName = "../../BuildOutput", // 出力先だけ変えました
                scenes = scenes,
            };

            //if (!Directory.Exists("Build"))
            //{
            //    Directory.CreateDirectory("Build");
            //}

            BuildPipeline.BuildPlayer(options);
        }
    }
}

これにより、以下コマンドでUnityのビルドができるようになります

const string unityExe = "\"C:\\Program Files\\Unity\\Hub\\Editor\\2021.2.8f1\\Editor\\Unity.exe\"";

await ($"{unityExe} -quit -batchmode " +
    $"-projectPath ../UnityProject " +
    $"-logfile - " + // コンソールにビルドのログを出す
    $"-executeMethod Sample.Builder.Build");

Unityのビルドスクリプトにパラメータを渡す

無料体験版のビルドとかアプリのバージョンとかをUnityに渡してビルドをしたい場合があると思います。
ビルドスクリプトでは System.Environment.GetCommandLineArgs() でビルド実行時のコマンドライン引数を参照できます。
https://forum.unity.com/threads/pass-custom-parameters-to-standalone-on-launch.429144/#post-3584029

まとめる

後は Program.cs を整理すれば、実行するだけでまとめてビルドしてくれる環境の出来上がりです。

さいごに

クラウド上のLinuxにビルド環境を構築すようとするとUnityのライセンスとかでもう少し複雑になりそうですが、自前のwindows上で行うくらいなら1日くらいで構築できました。

シェルは偉大ですが、使いなれている静的型付け言語のC#でこのようなスクリプトを組めるのは結構可能性を感じました。

Discussion