【ITニュース解説】Porting 5 vanilla-JS games to a web game portal without touching game code: notes on Playgama Bridge
2026年11月03日に「Dev.to」が公開したITニュース「Porting 5 vanilla-JS games to a web game portal without touching game code: notes on Playgama Bridge」について初心者にもわかりやすく解説しています。
ITニュース概要
既存のHTML5ゲームをWebゲームポータルに移植する際、ゲームコードに一切手を加えず、アダプター層とビルドスクリプトで対応した事例。API呼び出しの注入、ロード順序制御、ストレージ置換、音声管理、広告表示など、ポータル連携の具体的な技術的工夫を紹介。開発はAIが担当した。
ITニュース解説
既存のHTML5ゲームをWebゲームポータルに移植する際、元のゲームコードを一切変更せずに行うという興味深い取り組みが報告された。これは、システムエンジニアを目指す皆さんにとって、既存システムと外部サービスを連携させる際の課題と解決策を学ぶ良い機会となるだろう。
このプロジェクトの目的は、AIが作成した五つのシンプルなHTML5ゲーム(エンジンを使わず、プレーンなJavaScriptとWeb Audioで構築されたもの)を、広告収益を共有するWebゲームポータル「Playgama」に公開することだった。Playgamaは開発者向けのサンドボックス機能を提供しており、ゲームのZIPファイルをアップロードすると、すぐにサンドボックスページで公開される仕組みがある。しかし、メインカタログにゲームを掲載するには、特定の目標を達成する必要がある。具体的には、100回以上の有効なプレイ、平均5分以上のプレイ時間、そしてプレイあたり0.30回以上の広告表示だ。
このプロジェクトでは、ゲームコード自体には一切手を加えない、という厳格なルールが設定された。ポータル固有の動作はすべて「アダプターファイル」と呼ばれる一つのファイルと、「ビルドステップ」と呼ばれる作業に集約されている。これは、汎用性の高いゲームコードを保持しつつ、特定のプラットフォームの要求に対応するための、非常に実用的なアプローチである。
まず、ゲームがポータルとやり取りするための小さな「ポータルAPI」が用意された。これは、「Portal.start()」(ゲームプレイ開始)、「Portal.stop()」(ゲームプレイ終了または一時停止)、「Portal.happy()」(記録達成などの良い瞬間)、「Portal.midgame(cb)」(ゲームの中断時に広告表示の可能性があり、その後必ずcbを呼び出す)、「Portal.setAudio(ctx)」(オーディオコンテキストをアダプターに渡しミュート制御を可能にする)という五つのメソッドを持つオブジェクトだ。このAPIは、ポータル環境にSDK(ソフトウェア開発キット)が存在しない場合でもエラーを起こさないように設計されており、各呼び出しは何もしない(no-op)動作となる。そして、「midgame(cb)」は単にコールバック関数cbを実行するだけである。これにより、同じゲームビルドが、ポータルだけでなくGitHub Pagesのような汎用的な環境でも動作する。これらの呼び出しは、ビルドスクリプトによって、ゲームコード内の特定の場所に文字列パッチとして注入される。
次に、ゲームのロード順序の制御が課題となった。Playgamaでは、まず「Bridge」と呼ばれるSDKを初期化し、保存された進行状況(セーブデータ)を読み込み、その後プレイヤーが操作可能になった段階で「game_ready」というメッセージを送信する必要がある。この「game_ready」だけが必須だ。ゲームコードに触れずにこの順序を実現するための工夫は、ゲーム内のすべての<script>タグを、ブラウザが直接実行しない「type="text/x-pg-deferred"」という特殊なタイプに書き換えることだった。これにより、アダプターがBridgeの初期化とセーブデータの読み込みを待ってから、これらのスクリプトを適切な順序で実行できる。しかし、ここで一つ落とし穴があった。一般的なWebページでは「DOMContentLoaded」イベントを待って初期化処理を行うゲームが多いが、この方法ではアダプターがスクリプトを実行する頃には既にイベントが発火した後なので、ゲームの初期化処理が実行されない問題が発生する。これに対する解決策は、document.readyStateを確認するか、ビルド時に該当するコードを修正することだった。
セーブデータの管理も重要な課題である。ゲームは通常、ブラウザの「localStorage」という機能を使ってデータを保存するが、Playgamaでは独自のプラットフォームストレージを利用する必要がある。ここでも「ゲームコードに触れない」ルールが適用され、アダプターはwindow.localStorageを独自の「インメモリ・シム」に置き換えた。シムとは、本来の機能の代わりに同じような動作をするように作られたプログラムのことだ。このシムは、ゲーム起動時にポータルのbridge.storage.getを使ってすべてのセーブデータを一度に読み込み、ゲームがlocalStorageに書き込む際には、その変更を一時的にメモリに保持する。そして、変更が一定時間経過後にまとめてポータルのストレージに書き戻される(デバウンス処理)。これにより、ゲームコードはlocalStorageを使い続けていると錯覚したまま、実際のデータはポータルストレージに保存され、プレイヤーはデバイスをまたいでもセーブデータを引き継げるようになった。
オーディオの制御も複雑な要件の一つである。ポータルは、広告が表示されている間、タブが非表示になっている間、またはプラットフォーム側でオーディオがオフになっている間は、ゲームの音を停止することを要求する。複数の理由が同時に発生して音を止めたい場合があるため、アダプターはミュートが必要な「理由」のセットを管理し、そのセットが空でない限り、ゲームが生成したすべてのAudioContextを一時停止する仕組みを導入した。AudioContextはWeb Audio APIの中核であり、音の生成や制御を行うオブジェクトだ。また、ゲームがAudioContextのコンストラクタを呼び出す際に、アダプターがこれをラップすることで、ゲームが作成するすべてのAudioContextを自動的に追跡できるようにした。さらに、ミュートが必要な理由がまだ活動しているにもかかわらずゲームがctx.resume()を呼び出した場合、その再開要求は保留され、すべてのミュート理由が解消されるまで実行されないようになっている。
広告の表示に関しても、ユーザー体験を損なわないよう細心の注意が払われた。「midgame(cb)」というメソッドは、インタースティシャル広告(画面全体に表示される広告)を要求し、広告の表示状況に関わらず、必ずcbというコールバック関数を一度だけ実行する。これは、広告がスキップされた場合(例えば、短期間に何度も表示されないようにするための最小遅延期間が経過していない場合)、広告が閉じられたり失敗したりした場合、あるいは最悪の場合の45秒という安全タイムアウトが経過した場合でも、ゲームの進行が滞らないようにするためだ。また、報酬型広告(広告を見ることでゲーム内ヒントが得られるなど)は、プラットフォームがサポートしている場合にのみ提供され、サポートされていない場合はヒントが無料で提供されるようにすることで、プレイヤーが「ボタンを押しても何も起こらない」という状況を避けている。
他にも、いくつかの小さな課題が移植作業に時間を要した。例えば、Google Fontsのような外部フォントは、ポータルがゲームを実行する厳格なポリシーを持つiframe(Webページ内に別のWebページを埋め込む要素)内で正しく機能しないことがあったため、ビルド時にフォントファイルを自己ホストするように変更された。また、クロスオリジン分離ホスト(COOP/COEP)環境では、適切なCORS(クロスオリジンリソース共有)ヘッダーを持たない外部リクエストがページを壊す可能性があるため、ビルド時にすべての外部リクエストを排除する工夫も必要だった。複数のテストを同時に実行する際に、固定のポートが衝突して誤ったゲームが提供される問題も発生し、ランダムなポートを使用することで解決された。
このように、元のゲームコードに手を加えることなく、多様なプラットフォーム固有の要件に対応するためのアダプター層とビルドプロセスを構築する技術は、システムエンジニアリングの分野で広く応用できる重要なスキルである。既存のソフトウェア資産を最大限に活用しつつ、新しい環境に適応させるための設計思想や具体的な実装テクニックは、将来システム開発に携わる上で非常に役立つだろう。今回紹介された五つのゲームは現在Playgamaのサンドボックスで公開されており、特に平均プレイ時間を伸ばすためのフィードバックが求められている。ユーザーが直感的にゲームを理解し楽しめるかどうかが、プラットフォームでの成功の鍵を握っているのだ。