AIを使ったのに俺のゲームがリリースできないわけがない#3|ビルドを1か所から:BuildScriptを作ろう

上の段は今まで:人がエディタを開く → Build Profiles の Build を押す → 出来上がり。「画面とボタンが無いと、ビルド出来ない」。下の段はこれから:コマンドを1行 → BuildScript.Build が動く → 出来上がり。「画面が無いサーバー(UBA・GitHub Actions)でも、同じ手順でビルド出来る」
ボタンで押すビルドから、コマンド1行のビルドへ

前回は、星ふる夜の庭をスマホで遊べるようにしました。今回から、いよいよ自動化の話です!ビルドを自動にするには、まずボタンを押さなくてもビルド出来る形にしないといけません。そこで、ビルドの手順をスクリプトにまとめた BuildScript を作ります。バージョンやビルド番号を外から渡せるようにして、最後はコマンド1行でWindowsの.exeを作ってみたいと思います!

目次

なぜスクリプトでビルドするの?

今までのビルドは、エディタを開いて、Build Profiles の Build ボタンを押していました(初めてのビルド:Windows/Mac向け実行ファイルを作ろう【Unityスタートガイド10-2】)。

でも、このシリーズでビルドするのは、画面の無いサーバーです。

  • Unity Build Automation(UBA)や GitHub Actions のサーバーには、ボタンを押す人がいない
  • 代わりに、Unity をコマンドで起動して、決めたメソッドを呼んでもらう
  • そのメソッドの中に、ビルドの手順を全部書いておく

この「決めたメソッド」が、今回作る BuildScript です。ビルドの手順を1か所にまとめておけば、手元でもサーバーでも、同じビルドが出来ます。

BuildScriptを書こう

Assets の直下に Editor フォルダを作り、その中に BuildScript を作ります。Editor フォルダに入れたスクリプトはエディタの中だけで動き、ゲームのビルドには入りません。

using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;

public static class BuildScript
{
    // release.json の中身(ゲームごとに変わる値)
    [Serializable]
    class ReleaseConfig
    {
        public string productName;
        public string fileName;
    }

    // コマンドラインから呼ぶ入口。ビルド先は -buildTarget で決める
    public static void Build()
    {
        ReleaseConfig config = LoadConfig();
        string version = GetArgument("-appVersion", PlayerSettings.bundleVersion);
        int buildNumber = int.Parse(GetArgument("-buildNumber", "1"));

        PlayerSettings.productName = config.productName;
        ApplyVersion(version, buildNumber);

        BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
        var options = new BuildPlayerOptions
        {
            scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(scene => scene.path).ToArray(),
            locationPathName = GetOutputPath(target, config.fileName),
            target = target,
        };
        BuildReport report = BuildPipeline.BuildPlayer(options);
        Debug.Log($"[BuildScript] {target} {PlayerSettings.bundleVersion} ({buildNumber}) {report.summary.result} → {options.locationPathName}");
        if (report.summary.result != BuildResult.Succeeded)
        {
            EditorApplication.Exit(1);
        }
    }

    // バージョンとビルド番号を Player の設定に入れる。タグの「v1.0.0」のまま渡してもよい
    public static void ApplyVersion(string version, int buildNumber)
    {
        PlayerSettings.bundleVersion = version.TrimStart('v');
        PlayerSettings.Android.bundleVersionCode = buildNumber;
        PlayerSettings.iOS.buildNumber = buildNumber.ToString();
    }

    // ビルド先ごとの出力先(Android と iOS は、それぞれの回で足す)
    static string GetOutputPath(BuildTarget target, string fileName)
    {
        switch (target)
        {
            case BuildTarget.StandaloneWindows64:
                return $"Build/Windows/{fileName}.exe";
            case BuildTarget.WebGL:
                return "Build/Web";
            default:
                throw new ArgumentException($"このビルド先には、まだ対応していません:{target}");
        }
    }

    // コマンドラインの「-名前 値」から値を取る。無ければ defaultValue
    static string GetArgument(string name, string defaultValue)
    {
        string[] args = Environment.GetCommandLineArgs();
        int index = Array.IndexOf(args, name);
        if (index >= 0 && index + 1 < args.Length)
        {
            return args[index + 1];
        }
        return defaultValue;
    }

    static ReleaseConfig LoadConfig()
    {
        string json = File.ReadAllText("release.json");
        return JsonUtility.FromJson<ReleaseConfig>(json);
    }
}
BuildScript.Build の中で起きること。Unity.exe -batchmode(-buildTarget Win64 -appVersion v1.0.0 …)→ release.json を読む(ゲームの名前・ファイルの名前)→ ApplyVersion(バージョン 1.0.0・ビルド番号)→ BuildPipeline.BuildPlayer(シーンの一覧と出力先を渡す)→ 成功なら終了コード 0 で StarGarden.exe が出来る、失敗なら終了コード 1 でサーバーが止まる
Build メソッドの流れ
  • Build()
    • コマンドラインから呼ぶ入口。static のメソッドにしておくと、-executeMethod BuildScript.Build で呼べる
    • ビルド先(Windows・Webなど)は、起動するときの -buildTarget で切り替わったものを使う
    • シーンは、Build Profiles の Scene List でチェックが入っているものを、そのまま使う
  • BuildPipeline.BuildPlayer
    • Build ボタンを押したときと同じビルドを、スクリプトから行う
    • 結果(report.summary.result)が成功でなければ、EditorApplication.Exit(1) で終了コード1にして終わる
  • GetOutputPath
    • Windows は Build/Windows/StarGarden.exe、Web は Build/Web に出す
    • AndroidとiOSは、それぞれの回で足します。まだ対応していないビルド先では、エラーにして止める

ビルドに失敗したときに終了コードを1にしておくのが大事です。サーバーは終了コードを見て「成功したか」を判断するので、0のまま終わると、失敗したビルドを成功と思ってストアに上げようとしてしまいます。

バージョンとビルド番号を外から渡す

ストアに出すたびに、バージョンとビルド番号を上げる必要があります。毎回 Player の設定を手で書き換えるのは忘れやすいので、ビルドするときに外から渡すようにしました。

  • -appVersion v1.0.0:バージョン(Player の Version)
    • 先頭の v は取って入れるので、v1.0.0 のようなタグの名前でも、1.0.0 でも渡せる
  • -buildNumber 1:ビルド番号
    • Androidの versionCode と、iOSの Build に入る。ストアに上げるたびに必ず大きくする番号
    • 自動化したあとは、UBA のビルドの番号を入れます(#13)
  • GetArgument:Environment.GetCommandLineArgs() で起動したときの引数をもらい、名前の次の値を取り出す
Project SettingsのPlayerの上の部分。Company Name:DefaultCompany、Product Name:星ふる夜の庭、Version:1.0。Versionの欄を赤枠で囲む
ビルドするときに、この Version が外から渡した値になる

最初は引数の名前を -version にしていましたが、これは失敗でした。Unity自体に「-version:Unityのバージョンを表示して終わる」という引数があるので、ビルドせずに 6000.3.22f1 と出して終わってしまいます。自分で作る引数は、Unityの引数とぶつからない名前にしましょう(ここでは -appVersion)。

release.jsonを作ろう

ゲームの名前やファイルの名前のように、ゲームごとに変わる値は、スクリプトに書かずに設定ファイルに分けます。プロジェクトのフォルダの直下(Assets と同じ並び)に、release.json を作ります。

{
  "productName": "星ふる夜の庭",
  "fileName": "StarGarden"
}
unity-release/
├─ Assets/
│  └─ Editor/
│     └─ BuildScript.cs
├─ Packages/
├─ ProjectSettings/
└─ release.json   ← ここ
  • productName:ゲームの名前(Player の Product Name に入る)
  • fileName:ビルドしたファイルの名前(StarGarden.exe)
  • 文字コードは UTF-8 で保存する(日本語の名前が化けないように)

ほかのゲームにこの仕組みを持っていくときは、BuildScript はそのままで、release.json だけを書き換えればOKにしていきます。ストアのIDなども、回が進むごとにここへ足していきます。

batchmodeでビルドしてみよう

いよいよ、コマンドでビルドしてみます。ここでは Windows の PowerShell を使います。

同じプロジェクトをエディタで開いたままだと、コマンドのビルドは動きません。先にエディタを閉じてください。

PowerShell を開いて、次のように打ちます。$unity は Unity 本体の場所、$project は自分のプロジェクトの場所に変えてください。

$unity = "C:\Program Files\Unity\Hub\Editor\6000.3.22f1\Editor\Unity.exe"
$project = "D:\repositories\blog-anogame\projects\unity-release"

& $unity -batchmode -quit -projectPath $project -buildTarget Win64 `
  -executeMethod BuildScript.Build -appVersion v1.0.0 -buildNumber 1 `
  -logFile "$project\Logs\build.log" | Out-Null
echo $LASTEXITCODE
  • -batchmode:画面を出さずに起動する
  • -quit:メソッドが終わったら Unity を閉じる
  • -projectPath:開くプロジェクト
  • -buildTarget Win64:ビルド先を Windows(64bit)にしてから始める(Web なら WebGL)
  • -executeMethod BuildScript.Build:起動したら呼ぶメソッド
  • -logFile:ログを書き出す場所
  • | Out-Null:Unity が終わるまで待つため(これが無いと、PowerShell はUnityの終わりを待たずに次へ進んでしまう)

Unity の場所は、Unity Hub の Installs で、バージョンの右の歯車から「Show in Explorer」を選ぶと分かります。

Windows PowerShell の画面。Unity を batchmode で起動するコマンドのあと、echo $LASTEXITCODE で 0 が出る。Select-String でログから [BuildScript] の行を探すと「StandaloneWindows64 1.0.0 (1) Succeeded → Build/Windows/StarGarden.exe」。Get-ChildItem で Build\Windows を見ると、D3D12・MonoBleedingEdge・StarGarden.exe・StarGarden_Data・UnityCrashHandler64.exe・UnityPlayer.dll・星ふる夜の庭_BurstDebugInformation_DoNotShip が並ぶ
終了コードが 0 で、Build/Windows に StarGarden.exe が出来た

画面には何も出ませんが、1〜2分ほど待つと終わります。自分の環境では約1分20秒でした。

スライム

何も出ないまま待つの、ちょっと不安になりますね・・・。止まってないか、ログのファイルが増えていくのを見てました

  • echo $LASTEXITCODE が 0 なら成功(終了コード 0 が成功、それ以外は失敗)
  • ログ(Logs/build.log)の中の [BuildScript] の行に、ビルド先・バージョン・結果が出る
  • Build/Windows に StarGarden.exe が出来ている

StarGarden.exe をダブルクリックして、いつもの星ふる夜の庭が始まれば成功です!

星ふる夜の庭_BurstDebugInformation_DoNotShip というフォルダも出来ますが、名前のとおり、配るときには入れないフォルダです。配るときの zip の作り方は、itch.io と Steam の回でやります。

失敗したら終了コード1

Windows PowerShell の画面。-buildTarget Android で同じコマンドを動かすと、echo $LASTEXITCODE で 1 が出る。ログには「ArgumentException: このビルド先には、まだ対応していません:Android」と「executeMethod method BuildScript.Build threw exception.」
まだ対応していない Android を選ぶと、終了コード 1 で止まる

失敗したときに、ちゃんと分かるかも試しておきます。BuildScript がまだ対応していない Android(-buildTarget Android)でビルドすると、

  • GetOutputPath がエラーを出して、メソッドが止まる
  • Unity は「メソッドがエラーで止まった」として、終了コード 1で終わる

このように、ビルドの失敗も、メソッドの中のエラーも、終了コードで分かるようになりました。

手元で試すとPlayerの設定が書き換わる

git diff ProjectSettings/ProjectSettings.asset の結果。bundleVersion が 1.0 から 1.0.0 に、iPhone(iOSのビルド番号)が 0 から 1 に変わっている
ビルドすると、渡したバージョンが ProjectSettings に残る

BuildScript は Player の設定(Version など)を書き換えるので、手元でビルドすると ProjectSettings/ProjectSettings.asset が変わります。

  • サーバーでのビルドは毎回まっさらな状態から始まるので、気にしなくてOK
  • 手元で試したあとは、git で元に戻すか、そのまま気にせず使ってOK(次にビルドするときに、また上書きされる)
スライム

AIにコマンドを書いてもらったら -version で渡してきて、ビルドされないまま終わりました・・・

フェアリー

AIは「よくある名前」を付けがちなの。動かして、終了コードとログを見るところまでは自分でやろうね!

今回の完成チェック
– Assets/Editor に BuildScript、プロジェクトの直下に release.json がある
– エディタを閉じた状態で、コマンド1行でビルド出来る
– 終了コードが 0 で、Build/Windows/StarGarden.exe が出来て遊べる
– まだ対応していないビルド先を選ぶと、終了コードが 1 になる

次回は

次回は、このプロジェクトを GitHub に置いて、GitHub Actions をボタンで動かしてみたいと思います!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (1件)

コメントする

目次