マガジン

Godot 4のWeb書き出しでSharedArrayBufferエラーが出るときの対処

遊びをつくる

執筆にClaudeを使用しています。

小さなゲーム模型を四つの試験コースで動かし、修正を確かめる机。
記事のテーマをもとに制作したAI生成イラスト。

Godot 4でゲームをHTML5向けに書き出し、ブラウザで開いたとき「SharedArrayBuffer is not defined」や「Cross-Origin-Opener-Policy」に関するエラーが出て画面が真っ暗になる、という経験をした方は多いはずです。Godot 3までとは異なり、Godot 4のWeb書き出しはスレッド処理にSharedArrayBufferを使うため、サーバー側の設定が合っていないと動きません。 このエラーはGodotのバグではなく、ブラウザのセキュリティ仕様の問題です。具体的には「Cross-Origin Isolation(クロスオリジン分離)」と呼ばれる状態をサーバーが宣言しないと、ブラウザはSharedArrayBufferを無効化します。対処の方向性は「正しいHTTPヘッダーを返すようサーバーを設定する」か「スレッドなしのビルドを選ぶ」かの二択で、どちらを選ぶかはホスティング環境によって決まります。 以下では、エラーの仕組みの確認から、環境別の設定手順、スレッドなしビルドの使いどころまでを順に説明します。

この記事の目次
  1. なぜSharedArrayBufferが使えないのか
  2. 環境別:ヘッダーを返す設定方法
  3. ヘッダーを設定できない場合:スレッドなしビルドを選ぶ
  4. ローカルテストでも起きる問題と回避策
  5. よくある見落としポイント
  6. 次の行動
  7. 参考資料

なぜSharedArrayBufferが使えないのか

SharedArrayBufferは複数のWebワーカー(スレッド)が同一メモリ領域を読み書きするためのブラウザAPIです。2018年のMeltdown/Spectre脆弱性の発覚以降、ブラウザベンダーは悪用を防ぐためにこのAPIの利用条件を厳しくしました。現在のChrome・Firefox・Safariでは、ページが「Cross-Origin Isolated」な状態でないとSharedArrayBufferは使えません。

Cross-Origin Isolatedな状態とは、サーバーが次の二つのHTTPレスポンスヘッダーを返していることをブラウザが確認した状態を指します。

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

このヘッダーが返っているかどうかは、ブラウザの開発者ツール(DevTools)の「ネットワーク」タブでHTMLファイルのレスポンスヘッダーを見れば確認できます。cross-origin-opener-policy が same-origin に、cross-origin-embedder-policy が require-corp になっていれば設定済みです。JavaScriptコンソールで crossOriginIsolated と入力して true が返るかどうかでも確認できます。

環境別:ヘッダーを返す設定方法

自前サーバー(Apache)

.htaccess ファイルをゲームファイルと同じディレクトリに置き、以下を記述します。

Header set Cross-Origin-Opener-Policy "same-origin"
Header set Cross-Origin-Embedder-Policy "require-corp"

mod_headers が有効になっていない場合は、サーバー管理者か a2enmod headers で有効化が必要です。

自前サーバー(Nginx)

対象の location ブロックに以下を追加します。

add_header Cross-Origin-Opener-Policy "same-origin";
add_header Cross-Origin-Embedder-Policy "require-corp";

GitHub Pages

GitHub Pagesは現時点でカスタムHTTPヘッダーを直接設定する機能を持っていません。代わりに coi-serviceworker というオープンソースのService Workerを使う方法が広く使われています。GitHubリポジトリで公開されているスクリプトをHTMLファイルと同じディレクトリに配置し、Godotが生成したHTMLファイルの <head> 内に1行読み込みを追加します。Service WorkerがブラウザとページのやりとりにHTTPヘッダーを擬似的に付与する仕組みです。

ただし、この方法はサービスワーカーの登録に初回アクセスが必要なため、「最初の訪問ではロードできず、リロードすると動く」という挙動になります。本番公開より開発・テスト用途に向いています。

itch.io

itch.ioのプロジェクト設定(Upload files → Embed options)で「SharedArrayBuffer support」をオンにすると、itch.io側がヘッダーを付与してくれます。この設定をオンにするとゲームはitch.ioのサンドボックス内で動くため、外部ドメインのリソースを読み込む場合は制限が生じることがあります。詳細は公式ドキュメントで確認してください。

Netlify

リポジトリのルートに netlify.toml を置き、以下を記述します。

toml
[[headers]]
  for = "/*"
  [headers.values]
    Cross-Origin-Opener-Policy = "same-origin"
    Cross-Origin-Embedder-Policy = "require-corp"

アソバース

アソバースにZIPファイルとして投稿する場合は、アソバース側のサーバー設定が適用されます。Godot 4のWeb書き出しがそのまま動作するかどうかはホスティング環境の設定に依存するため、投稿前に公式のヘルプやお知らせで対応状況を確認してください。

ヘッダーを設定できない場合:スレッドなしビルドを選ぶ

ホスティング環境を変えられない、Service Workerも使いたくないという場合は、Godot 4の「スレッドなし(no-threads)書き出し」を使う方法があります。

Godotエディタで「プロジェクト → エクスポート」を開き、Webプラットフォームの設定の中に「スレッドサポートを使用する(Use Threads)」というチェックボックスがあります。これをオフにして書き出すと、SharedArrayBufferを使わないビルドが生成されます。

スレッドなしビルドの主な制約は次の通りです。

  • GDScriptの Thread クラスが使えない
  • Audioサーバーがメインスレッドのみになるため、音の処理が重いシーンでフレームレートに影響が出ることがある
  • Godot 4.3以降では一部の処理でスレッドを前提とした最適化が効かなくなる

カジュアルなパズルゲームや短いアクションゲームなど、マルチスレッドを積極的に使っていない作品であれば、スレッドなしビルドで実用上の問題はほとんど出ません。自分のゲームがスレッドを明示的に使っているかどうかは、プロジェクト内で Thread.new() を呼んでいる箇所があるかどうかで確認できます。

ローカルテストでも起きる問題と回避策

index.html をブラウザにドラッグ&ドロップしてテストすると、file:// プロトコルではSharedArrayBufferが使えないためエラーになります。ローカルでのテストには必ずローカルサーバーを使ってください。

Godotエディタの「エクスポート → HTML5でプレイ」ボタンを使うと、Godotが内蔵の簡易サーバーを立ち上げてくれるため、これが最も手軽です。コマンドラインで自分でサーバーを立てる場合は、PythonやNode.jsの簡易サーバーではヘッダーが付かないことが多いため、coi-serviceworker を組み込むか、対応ヘッダーを返せる設定を追加する必要があります。

よくある見落としポイント

設定を行ったのにまだエラーが出るとき、次の点を順に確認すると原因が見つかりやすくなります。

  1. ヘッダーがHTMLファイルだけに付いていてJSやWASMには付いていない:すべてのレスポンスにヘッダーが必要です。Apache・Nginxでは /* を対象にするか、対象ディレクトリをまとめて指定します。
  2. CDNやリバースプロキシがヘッダーを除去している:CloudflareなどのCDNが間に入っている場合、CDN側でもヘッダーのパススルーを確認します。
  3. ブラウザキャッシュが古いレスポンスを使っている:DevToolsで「ネットワーク → キャッシュを無効化」にチェックを入れてリロードします。
  4. Safariの古いバージョン:SafariはiOS 15.2以前ではSharedArrayBufferのサポートが不完全です。対象ユーザーにiOSが多い場合は、スレッドなしビルドを検討します。

次の行動

まずブラウザのDevToolsでHTMLファイルのレスポンスヘッダーを確認し、cross-origin-opener-policy と cross-origin-embedder-policy の値を見てください。ヘッダーがなければ、使っているホスティング環境に合った設定を本記事の手順で追加してください。マルチスレッドを使っていない作品であれば、スレッドなしビルドへ切り替えることで設定を省けます。作ったゲームをアソバースやitch.ioなどのプラットフォームに投稿する際は、投稿先のSharedArrayBuffer対応状況をあらかじめ公式ページで確認してから書き出し設定を決めると、差し戻しを避けられます。

参考資料

  • Godot Engine 公式ドキュメント(Exporting for the Web)
  • MDN Web Docs「SharedArrayBuffer」
  • MDN Web Docs「Cross-Origin Isolation guide」
  • coi-serviceworker(GitHub リポジトリ)
  • itch.io Creator Documentation(Shared Array Buffer support)