この物語はフィクションです。
非プログラマー 「なんかバグってる!直して!」
プログラマー 「どれどれ?バージョンは?」
非プログラマー 「えーっと、ちょっと前のやつ!」
プログラマー (ちょっと前っていつのだよ?何回ビルドしたと思ってんだよ!?)
はじめに
先ほどの物語はフィクションではありますが、モバイルゲームアプリの開発現場において、このような不幸な経験がある方も多いのではないでしょうか?
解決策の一つはバージョンをゲームアプリ内に表記して、それを報告してもらうことです。(もちろん端末のアプリ管理機能を使って調べてもらうのもありですね。)
しかし、これはビルドごとにバージョンを適切に更新している必要があります。
また、もし製品版やデバック版、ステージング版など複数のビルド設定がある場合、不具合が発生したのがどのビルド版なのかを把握する必要もあります。
前述のフィクション
の物語が起きないようにするための、解決方法・対処方法はさまざまなものが考えられますね。
もしあなたがUnity Cloud Buildを使っているのであれば、私のおすすめは、CloudBuildManifest
を用いて、ビルドターゲットとビルド番号をアプリ内に表記するという方法です。
この投稿では、Unity Cloud BuildのCloud BUild Manifest
とそれを用いたTipsを紹介します。
CloudBuildManifestとは?
CloudBuildManifestについて説明します。
Unity Cloud Buildを行うと、Assets/__UnityCloud__/Resources
以下にUnityCloudBuildManifest.json
というJSON形式のテキストファイルが自動で置かれます。
生成されるJSONの例を示します。
{
"scmCommitId":"abcdefghijklmnopqrstuvwxwz01234567890123",
"scmBranch":"master",
"buildNumber":"3",
"buildStartTime":"1/1/2017 11:11:11 PM",
"projectId":"cloud_build_example",
"bundleId":"com.teammurosta.cloud_build_example",
"unityVersion":"5.5.0f3",
"xcodeVersion":"6.3.1",
"cloudBuildTargetName":"default-ios",
}
それぞれの要素は次の通りです。
- scmCommitId: Unity Cloud Build によってビルドされた commit、または changelist
- scmBranch: ビルドされたブランチ名
- buildNumber: このビルドに関連する Unity Cloud Build 番号
- buildStartTime: ビルドプロセスが始まったときの UTC timestamp
- projectId: Unity Cloud Build プロジェクト識別子
- bundleId: (iOS/Androidのみ) bundleIdentifier は Unity Cloud Build 内で設定されます
- unityVersion: Unity Cloud Build がビルド作成に使用した Unity のバージョン
- xcodeVersion: (iOS のみ) ビルドに使用される XCode のバージョン
- cloudBuildTargetName: ビルドされたプロジェクトビルドターゲットの名前。
この生成されるJSON(CloudBuildManifest)の要素には、ビルドしたコミットのリビジョン、ビルドに使ったUnityやXcodeのバージョン、ビルド設定であるビルドターゲット名がありますね。
これらの情報をゲームアプリ内で表示し、それを報告してもらえばいろいろ解決しそうですね。
私のおすすめは、cloudBuildTargetName
とbuildNumber
を併記してゲームアプリ内に表示する方法です。
Unity Package Manager経由でインストール
このCloudBuildManifestを取得するライブラリを、Unity Package形式でGitHubに公開しています。
インストールするには、Unity 2019.1以上のUnityでかつ、Gitがインストールされている環境において、次の一文をPackages/manifest.json
内のdependencies
に追加してください。
"com.ryotamurohoshi.cloudbuildmanifest": "https://github.com/RyotaMurohoshi/CloudBuildManifest.git#TAG_YOU_WANT_TO_USE",
TAG_YOU_WANT_TO_USE
は、利用したいGitHub上のTagを指定してください。
使い方は次の通りです。
string buildInfoText;
if (BuildManifest.HasBuildManifest())
{
var buildManifest = BuildManifest.Load();
buildInfoText = $"{buildManifest.CloudBuildTargetName} #{buildManifest.BuildNumber}";
}
else
{
buildInfoText = "Not Cloud Build";
}
コード内容
もしUnity 2019.1よりも古いUnityを使っているなどで、上記のライブラリが使えない場合は、つぎのソースコードをプロジェクトに入れてください。
using System;
using UnityEngine;
#pragma warning disable 649
namespace CloudBuildManifest {
/// <summary>Class <c>BuildManifest</c> provides build information for Unity Cloud Build.</summary>
[Serializable]
public class BuildManifest
{
/// <summary>
/// If binary is built with Unity Cloud Build, static method <c>Load</c> returns the BuildManifest instance.
/// If binary is not built with Unity Cloud Build, static method <c>Load</c> returns null.
/// </summary>
/// <returns>The BuildManifest instance or null.</returns>
public static BuildManifest Load()
{
var json = LoadManifestAsset();
if (json == null)
{
return null;
}
else
{
return JsonUtility.FromJson<BuildManifest>(json.text);
}
}
/// <summary>
/// If binary is built with Unity Cloud Build, static method <c>HasBuildManifest</c> returns true.
/// If binary is not built with Unity Cloud Build, static method <c>HasBuildManifest</c> returns false.
/// </summary>
/// <returns>boolean</returns>
public static bool HasBuildManifest()
{
return LoadManifestAsset() != null;
}
private static TextAsset LoadManifestAsset()
{
return Resources.Load<TextAsset>("UnityCloudBuildManifest.json");
}
[SerializeField]
string scmCommitId;
/// <value>Property <c>ScmCommitId</c> represents the commit or changelist that was built.</value>
public string ScmCommitId { get { return scmCommitId; } }
[SerializeField]
string scmBranch;
/// <value>Property <c>ScmBranch</c> represents the name of the branch that was built.</value>
public string ScmBranch { get { return scmBranch; } }
[SerializeField]
string buildNumber;
/// <value>Property <c>BuildNumber</c> represents The Unity Cloud Build “build number” corresponding to this build.</value>
public string BuildNumber { get { return buildNumber; } }
[SerializeField]
string buildStartTime;
/// <value>Property <c>BuildStartTime</c> represents The UTC timestamp when the build process was started.</value>
public string BuildStartTime { get { return buildStartTime; } }
[SerializeField]
string projectId;
/// <value>Property <c>ProjectId</c> represents The Unity project identifier.</value>
public string ProjectId { get { return projectId; } }
[SerializeField]
string bundleId;
/// <value>Property <c>BundleId</c> represents The bundleIdentifier configured in Unity Cloud Build (iOS and Android only).</value>
public string BundleId { get { return bundleId; } }
[SerializeField]
string unityVersion;
/// <value>Property <c>UnityVersion</c> represents The version of Unity that Unity Cloud Build used to create the build.</value>
public string UnityVersion { get { return unityVersion; } }
[SerializeField]
string xcodeVersion;
/// <value>Property <c>XCodeVersion</c> represents The version of XCode used to build the project (iOS only).</value>
public string XCodeVersion { get { return xcodeVersion; } }
[SerializeField]
string cloudBuildTargetName;
/// <value>Property <c>CloudBuildTargetName</c> represents the name of the build target that was built.</value>
public string CloudBuildTargetName { get { return cloudBuildTargetName; } }
}
}
#pragma warning restore 649
ScriptableObjectも生成される
実は組み込みでCloud Build Manifestを司るクラスが存在します。
UnityEngine.CloudBuild.BuildManifestObject
というScriptableObjectを継承したクラスです。
このクラスはすこし癖があります。UNITY_CLOUD_BUILD
ディレクティブ内でしか使えないのです。
このBuildManifestObject
を利用したコードを次に示します。
using UnityEngine;
using UnityEngine.UI;
#if UNITY_CLOUD_BUILD
using UnityEngine.CloudBuild;
#endif
public class ManifestExample : MonoBehaviour
{
[SerializeField]
Text text;
void Start()
{
#if UNITY_CLOUD_BUILD
var manifest = Resources.Load<BuildManifestObject>("UnityCloudBuildManifest.scriptable");
text.text = manifest.ToJson();
#endif
}
}
UNITY_CLOUD_BUILD
というディレクティブに注目してください。これはCloud Buildで有効になります。
BuildManifestObjectクラスのScriptableObjectが、Assets/__UnityCloud__/Resources
以下にUnityCloudBuildManifest.scriptable
という名前で生成されます。
BuildManifestObjectは、Cloud Buildでないと使えないクラスですので、UNITY_CLOUD_BUILD
ディレクティブとともに使う必要があるという点に注意してください。
ドキュメントによると、BuildManifestObject
には次のメンバがあるようです。
namespace UnityEngine.CloudBuild
{
public class BuildManifestObject : ScriptableObject
{
// Tries to get a manifest value - returns true if key was found and could be cast to type T, false otherwise.
public bool TryGetValue<T>(string key, out T result);
// Retrieve a manifest value or throw an exception if the given key isn't found.
public T GetValue<T>(string key);
// Sets the value for a given key.
public void SetValue(string key, object value);
// Copy values from a dictionary. ToString() will be called on dictionary values before being stored.
public void SetValues(Dictionary<string, object> sourceDict);
// Remove all key/value pairs
public void ClearValues();
// Returns a Dictionary that represents the current BuildManifestObject
public Dictionary<string, object> ToDictionary();
// Returns a JSON formatted string that represents the current BuildManifestObject
public string ToJson();
// Returns an INI formatted string that represents the current BuildManifestObject
public override string ToString();
}
}
公式ドキュメントには
JSON TextAsset はまだ書き出されていないため、BuildManifestObject は、Unity Cloud Build によってに呼び出されるエクスポート前メソッドのオプショナルパラメーターとしても使用されます。
とあります。
ビルド前処理などのEditor拡張のコードなどでは、Textアセットではなく、こちらのBuildManifestObject
を使う必要があるようです。
さいごに
CloudBuildManifest
を用いれば、
「あれ、これどのバージョンだっけ!?」
ということが避けられそうですね!