ProcessXでUnity埋め込みネイティブアプリのビルド手順を自動化させる
ProcessX は UniTask
や MagicOnion
の開発・メンテを行っている Cysharp の OSS のひとつです。
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#で記述することができるようです。
筆者はとある事情で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 導入は終わりです。
あとは試しに、
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
の対応範囲が変わってしまうのでコンパイルエラーになります。見やすくするために改行するときは ()
で囲ってやりましょう。
args
で参照する
ポイント2 コマンドライン引数は 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での使い方になるようです)
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()
でビルド実行時のコマンドライン引数を参照できます。
まとめる
後は Program.cs を整理すれば、実行するだけでまとめてビルドしてくれる環境の出来上がりです。
さいごに
クラウド上のLinuxにビルド環境を構築すようとするとUnityのライセンスとかでもう少し複雑になりそうですが、自前のwindows上で行うくらいなら1日くらいで構築できました。
シェルは偉大ですが、使いなれている静的型付け言語のC#でこのようなスクリプトを組めるのは結構可能性を感じました。
Discussion