Unity WebGL を S3 + CloudFront に置くと真っ黒な画面のまま動かない理由
Brotli 圧縮した Unity WebGL ビルドを S3 に置いただけでは動きません。Content-Encoding ヘッダーの設定と、アップロード時に指定すべき値をまとめます。
- Unity
- WebGL
- AWS
- S3
- CloudFront
Unity で WebGL ビルドを吐いて、出てきたフォルダをそのまま S3 にアップロードし、 CloudFront 経由で開くと、ローディングバーすら出ずに真っ黒な画面のまま 止まることがあります。原因と解決方法をまとめておきます。
何が起きているか
このとき、ブラウザのコンソールには次のようなエラーが出ます。
Unable to parse Build/xxxxx.framework.js.br! This can happen if build compression was enabled but web server hosting the file did not serve the file with a HTTP Response Header Content-Encoding.
エラーメッセージが親切で、実は答えがそのまま書いてあります。 ビルド時に圧縮したのに、サーバーが「これは圧縮されています」と 伝えていない、ということです。
なぜ起きるのか
Unity の WebGL ビルドは、初期設定で Brotli 圧縮がかかります。 出力されるファイルは次のような名前になります。
Build/
project.loader.js
project.framework.js.br
project.data.br
project.wasm.br
末尾に .br が付いているとおり、これらは Brotli で圧縮済みのファイルです。
ブラウザはこれを受け取ったとき、レスポンスヘッダーに
Content-Encoding: br が付いていて初めて「解凍してから読む」と判断します。
ここが落とし穴で、S3 はファイルの拡張子を見て自動的にヘッダーを
付けてはくれません。何も指定せずにアップロードすると、
Content-Type: binary/octet-stream が付くだけで、
Content-Encoding は空のままです。ブラウザは圧縮されたバイト列を
そのまま JavaScript や WebAssembly として解釈しようとして失敗し、
結果として何も表示されないまま止まります。
ローカルで Unity の Build And Run を使ったときに動くのは、 Unity が立てる簡易サーバーが必要なヘッダーを自動で付けてくれるからです。 だから「ローカルでは動くのに本番だけ動かない」という状況になります。
解決方法その1:アップロード時にヘッダーを明示する
S3 にアップロードするときに、拡張子ごとに正しいヘッダーを指定します。 指定すべき組み合わせは次のとおりです。
| ファイル | Content-Type | Content-Encoding |
|---|---|---|
.wasm.br | application/wasm | br |
.js.br | application/javascript | br |
.data.br | application/octet-stream | br |
.symbols.json.br | application/json | br |
Gzip 圧縮を選んでいる場合は、拡張子が .gz になり、
Content-Encoding は gzip になります。考え方はまったく同じです。
重要なのは、Content-Type は圧縮前の中身の型を書くという点です。
.wasm.br に対して application/brotli のようなものを書くのではなく、
あくまで中身が WebAssembly なので application/wasm を指定します。
圧縮されているという情報は Content-Encoding のほうだけが担当します。
解決方法その2:Decompression Fallback を有効にする
Unity 側の設定で逃げる方法もあります。Player Settings の Publishing Settings にある Decompression Fallback を有効にすると、 サーバーが何のヘッダーも返さなくても、JavaScript 側で解凍してから 読み込むようになります。
ただしこれは推奨しません。解凍用のコードがビルドに同梱されるため サイズが増えますし、ブラウザのネイティブな解凍処理ではなく JavaScript での解凍になるので、起動が明確に遅くなります。 数十メガバイトある WebGL ビルドでこれをやると、待ち時間が体感で 分かるレベルで伸びます。ヘッダーを正しく設定できる環境なら、 設定したほうが確実に速くなります。
CloudFront の自動圧縮との関係
CloudFront には転送データを自動で圧縮する機能がありますが、
これはすでに Content-Encoding が付いているオブジェクトには
適用されません。したがって二重圧縮を心配する必要はなく、
自動圧縮は有効のままにしておいて問題ありません。
むしろ自動圧縮は、index.html や CSS など、圧縮していない
テキストファイルに効いてくれるので有効にしておくほうが得です。
設定を変えたあとの注意
ヘッダーを直したのに症状が変わらない場合、CloudFront が古いレスポンスを キャッシュしたままになっている可能性があります。ヘッダーの修正は キャッシュされた内容には遡って反映されないので、該当パスの キャッシュ削除を実行してから、ブラウザ側もキャッシュを無視して 再読み込みして確認してください。
ここで「まだ直っていない」と勘違いして設定をいじり回すと、 何が効いたのか分からなくなります。正しく直っていた設定を、 それと気づかずに元に戻してしまうこともあるので注意してください。