🐸

unityroomでAddressableに対応するためのテンプレート作った

2022/06/02に公開

背景

unityroomで英語翻訳のためにLocalizationパッケージを導入したゲームをアップロードしようとしたのですが、このパッケージはAddressableに依存しています。
このAddressableを使ったときに追加でビルドされる./StreamingAssetsのアップロードには、現状unityroom本体では対応しておらず、WebGLビルドの加工や、自前でサーバーを用意して読み込ませる必要があることが分かりました。

unityroom本体でのAddressable対応を待つ...でも良かったのですが、既にunityroomでAddressableを使ってみようとする記事をちょこちょこ見かけたので、それらを参考に、

  1. unityroom用に必要なビルド後処理
  2. 1のビルドとStreamingAssetsのサーバへのアップロード

を、なるべく簡単に(サーバー代もお安く)できる仕組みを作ってみることにしました。

ソースコード

https://github.com/uimss/UnityroomAddressableTemplate

2Dプロジェクトです。パッケージ化とかはしてませんめんどうなので

使い方

今回、自動ビルドとサーバーアップロードにはGithub ActionsCloudflare Pagesを使うのでアカウント登録が必要です。
どちらも個人で使う分には無料アカウントで充分ですが、一応制限があります(後述)

GithubリポジトリとCloudflare Pagesの準備

メインのGithubリポジトリを作成

自動ビルド用に、Unityプロジェクトを管理するGithubリポジトリを作成します。

(既にある場合はスルーしてください)
(今回はGithub Actionsしか使わないので、実はself-hosted runnerを使えばローカルでいいのではという説がありますが、未検証です)

公開用Githubリポジトリを作成

ビルドしたStreamingAssetsを配置するためのもので、privateでもOKです。
今回は公開用のブランチcf-pagesを作成しておきます。

Cloudflare Pagesのプロジェクトを作成

Cloudflare Pagesはまだ情報が少なめなので画像付きで紹介します。

ダッシュボードにログインして Pages に飛び、Create Project から Connect to Git を選びます。

連携するGithubリポジトリを選ぶと、以下のようなプロジェクト設定画面が出ます。
Production branch に公開用ブランチの名前 cf-page
Build output directory に公開ディレクトリの名前 public
を設定して、プロジェクトを登録します。
(同時に初回のビルドが走りますが、まだリポジトリに何もないのでスルーしてください)

登録が完了して Pages の最初のページに戻ってくると、連携しているリポジトリ名の下に
Cloudflare Pagesプロジェクトのドメインが表示されます。

ビルド後処理のセットアップ

ソースのUnityプロジェクトをまるっとコピーするか、Assets/UnityroomBuilder/を使いたいプロジェクトのAssets/以下のどこかに配置します。

その後 Edit -> Project Settings でプロジェクト設定ウィンドウを開き、Unityroom Builder タブで Steaming Assets Urlを設定します。

{version}にはUnityのApplication.versionが自動で入るので、今回は後々の自動サーバーアップロードの仕様に合わせて、以下のような設定にします。

https://Cloudflare Pagesプロジェクトのドメイン/{version}/StreamingAssets

ビルド後処理をローカルで実行する

ためしにビルド後処理をローカルでも動かしてみます。
通常のビルド設定で、ビルドターゲットをWebGLに変更し、Tool -> Build WebGL for unityroom を選びます。

後処理の結果、ビルド内の*.loader.jsの内容の一部が設定したURLに置換されます。
通常のビルドウィンドウからビルドした場合、後処理は行われません。

自動ビルド&アップロードのセットアップ

だいぶ簡略化できましたがそれでも長めです...。
ポチポチしたら終わる(はず)なのでがんばりましょう。

Github Actionsのワークフロー作成

ソース内の.github/workflow/にGithub Actions用の定義ファイルがあるので
これをまるっと使いたいプロジェクトの直下に配置します。

以下の2つのファイルはプロジェクトに合わせて一部変更が必要です。

.github/workflows/cloudflare.yml
  PUBLISH_REPOSITORY: your-account/your-project-repo # 公開用のGithubリポジトリ名
.github/workflows/main.yml
  PROJECT_NAME: your-project # 自分のプロジェクト名。なんでもOK
  PUBLISH_REPOSITORY: your-account/your-project-repo # 公開用のGithubリポジトリ名

上記の設定ができたらGithubにpushします。

Unityライセンスの認証

定義ファイルをpushすると、GithubのActionsタブに3つのワークフローが登録されます。

Github上でUnityをビルドするにはライセンス認証が必要なので
以下を参考にAcquire activation fileワークフローを実行し、Unityのulf認証ファイルを取得します。(Unityのバージョンは自動でセットされるので、以下の記事にあるUnityバージョンの修正は不要です)

https://zenn.dev/nikaera/articles/unity-gameci-github-actions#activation%3A-gameci-で必要となる-unity-license-のアクティベーションを行う

Github ActionsのSecrets登録

ワークフローの実行に必要な認証情報をSecretsに登録します。

  • PERSONAL_TOKEN : Githubの個人トークン
  • UNITY_EMAIL : Unityのログイン用メールアドレス
  • UNITY_PASSWORD : Unityのログイン用パスワード
  • UNITY_LICENSE : ulf認証ファイルの中身をそのままコピペ

最終的にこんな感じになります。

Cloudflare Pagesの基本ワークフロー実行

ワークフロー一覧画面に戻り、Publish Cloudflare base filesを実行します。これによりhttps://**Cloudflare Pagesプロジェクトのドメイン**/へのHTTPリクエストで、HTTPレスポンスにCORSヘッダーが返却されるようになります。
CORS対応をすることで、unityroomなどの外部サイトからもアセットが取得できるようになります。

自動ビルド&アップロードを実行する

ワークフロー一覧にあるBuild WebGL for unityroom and Publish StreamingAssetsが自動ビルド&アップロード用のワークフローです。
ここまでの設定を行ったあとに実行すると、リモートでunityroom用のWebGLビルドが作成され、StreamingAssets以下のビルドファイルがhttps://**Cloudflare Pagesプロジェクトのドメイン**/{version}/StreamingAssets/**ビルドファイル名**でアクセスできるようになります。

もしくは、メインのGithubリポジトリにciブランチを用意して、ビルドしたいブランチをciブランチにマージすると、上記のワークフローが自動的に実行されます。

unityroomに本体をアップロードする

unityroom用にビルドしたゲーム本体を、通常通りの手順でアップします。
本体はローカルでビルドしたものでもOKですが、Build WebGL for unityroom and Publish StreamingAssetsワークフローを実行すると、ゲーム本体のほうもビルドされてGithub Actionsからダウンロードできるようになっています。

最後にゲームの起動確認を行います。もしアセットのロードが失敗している場合、まずはブラウザのDeveloper Tools -> Network からsetting.jsonの読み込み結果を確認してみましょう。

大丈夫そうならこれで完了です。おつかれさまでした!

補足1: Github Actions と Cloudflare Pagesの制限

Github ActionsもCloudflare Pagesも基本無料ですが、やっぱりサーバなので制限があります。
Cloudflare Pagesは転送量自体は無制限(すごい)なので、一旦アップロードした分に関してはそうそう問題にはならないかと思いますが、Addressable用としては特にファイルサイズ制限(Cloudflare Pagesが25MB、Githubが100MB)がやや小さめで、内容次第では個人でも引っかかることがありえそうです。
また、高頻度でのビルド実行やビルド成果物のサイズが大きなプロジェクトも要注意です。

https://docs.github.com/ja/billing/managing-billing-for-github-actions/about-billing-for-github-actions

https://developers.cloudflare.com/pages/platform/limits/

補足2: Strip Engine Code の対応

自分は再現しませんでした(Unity 2020.3.16f)が、プロジェクトによってはStrip Engine Code機能によって必要なコードなのに削除される可能性があるそうです。その場合、以下の記事のように適宜link.xmlを作成するのがよさそうです。

https://qiita.com/lycoris102/items/d7ccf683c0d0552b74a7


ここからはおまけ。

WebGLでのAddressable(AssetBundle)読込の流れ

Addressableは、アセットを環境によってローカルに置くかリモートに置くか選べるやつでは…?と当初思っていたのですが、WebGLにおいては「アセットがどこに配置されているか」を定義するファイル(たぶんStreamingAssets/aa/setting.jsonStreamingAssets/aa/catalog.json)はデフォルトでは必ずローカル側にあることが想定された作りになっています。

index.html
-> Build/*.loader.js
-> StreamingAssets/aa/setting.json
...

この挙動をUnityが提供している機能で変更する場合、独自のWebGLテンプレートを用意して読み込み先を書き換えることになります。

https://docs.unity3d.com/ja/2020.3/Manual/webgl-templates.html

DefaultのWebGLテンプレートでいうここら辺ですね。

index.html
      var config = {
        dataUrl: buildUrl + "/{{{ DATA_FILENAME }}}",
        frameworkUrl: buildUrl + "/{{{ FRAMEWORK_FILENAME }}}",
        codeUrl: buildUrl + "/{{{ CODE_FILENAME }}}",
#if MEMORY_FILENAME
        memoryUrl: buildUrl + "/{{{ MEMORY_FILENAME }}}",
#endif
#if SYMBOLS_FILENAME
        symbolsUrl: buildUrl + "/{{{ SYMBOLS_FILENAME }}}",
#endif
        streamingAssetsUrl: "StreamingAssets", // ここを書き換えたい
        companyName: "{{{ COMPANY_NAME }}}",
        productName: "{{{ PRODUCT_NAME }}}",
        productVersion: "{{{ PRODUCT_VERSION }}}",
      };

………が、unityroomにはそもそもビルド成果物のうちindex.htmlはアップロードせず、unityroom側で固定で用意されているhtmlから各種ファイルがロードされることになります。
そのため、ビルド後処理では*.loader.jsの中身を以下のように気合いで書き換えています。

UnityroomBuilder.cs
// NOTICE: Deeply dependent on loader.js generation logic
var originalUrl = $"new URL(l.streamingAssetsUrl,document.URL)";
var newUrl = $"new URL(\"{newStreamingAssetUrl}\")";
Debug.Log($"Replace StreamingAssetUrl file: {loaderJsPath} from: {originalUrl} to: {newUrl}");
ReplaceStringInFile(loaderJsPath, originalUrl, newUrl);

SettingsProviderによるプロジェクト設定の拡張

以下を参考に実装しました。

https://qiita.com/sune2/items/a88cdee6e9a86652137c

ScriptableSingletonと組み合わせることで結構スッキリ書けて便利でした

https://github.com/uimss/UnityroomAddressableTemplate/blob/main/Assets/UnityroomBuilder/Editor/UnityroomBuilderSettings.cs

Addressableを使ったほうがよいか

WebGLでは、最初に書いたような何らかの背景があるか、脱Resourcesしてでも起動を早くしたい場合以外は使わなくていい気がします…!
※執筆時点での個人のかんそうです

参考

実装にあたり、特にお世話になったページたち

https://game.ci/docs/github/builder
https://github.com/marketplace/actions/github-pages-action

Discussion