マガジン

Pygameで作ったゲームをブラウザで動かす:pygbagの使い方

遊びをつくる

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

三つの透明な箱に並ぶ入力キー、球の動く仕組み、小さなゲームの舞台を赤い糸でつないだ模型。
記事のテーマをもとに制作したAI生成の写真風イメージ。

PythonのゲームライブラリであるPygameで作ったゲームは、そのままではブラウザで動きません。相手のPCにPythonをインストールしてもらう必要があり、友人や不特定多数に遊んでもらうには大きな壁になります。この問題を解決するのがpygbagです。pygbagはPygameのコードをWebAssembly(ブラウザ上でネイティブに近い速度でコードを実行する仕組み)に変換し、HTMLファイルとして書き出すPythonパッケージです。 pygbagを使うと、Pygameプロジェクトをブラウザで直接プレイできるZIPまたはHTMLの形に変換できます。変換後のファイルはアソバースのような投稿プラットフォームにそのまま出せるため、「作ったあとどうやって公開するか」という問題も同時に片付きます。この記事では、pygbagが動く条件の確認から変換の手順、よく詰まるポイントの対処までを順に説明します。

この記事の目次
  1. pygbagが動く条件を先に確認する
  2. メインループを非同期に書き換える
  3. pygbagをインストールして変換を実行する
  4. よく詰まるポイントと対処法
  5. 変換後のゲームを公開する
  6. 次の行動
  7. 参考資料

pygbagが動く条件を先に確認する

変換を始める前に、手元のプロジェクトが対応できるかどうかを確認しておくと、後で時間を無駄にせずに済みます。

pygbagが前提とする主な条件は次のとおりです。

  • Pythonのバージョン:pygbagは内部でPyodide(PythonをWebAssemblyで動かすランタイム)を利用します。対応するPythonバージョンはpygbagのリリースによって変わるため、公式ドキュメントで現在の対応状況を確認してください。
  • Pygameのバージョン:pygame-ce(コミュニティ版)への移行を推奨している時期もあり、バージョンの組み合わせによっては警告が出ます。
  • 非同期処理への書き換え:pygbagで動かすには、メインループをasync/await構文に対応させる必要があります。これが最大の変更点です。
  • 外部ライブラリの制限:NumPyなどPyodideが対応しているパッケージは使えますが、C拡張に強く依存するライブラリや、ブラウザのセキュリティ制限に引っかかるファイルI/Oを使うライブラリは動かない場合があります。

逆に言えば、シンプルなループと標準的なPygame APIだけで書かれたゲームであれば、比較的少ない変更で変換できます。

メインループを非同期に書き換える

pygbagの変換でほぼ必ず必要になるのが、メインループのasync化です。通常のPygameゲームは次のような構造を持っています。

python
import pygame

def main():
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        screen.fill((0, 0, 0))
        pygame.display.flip()
        clock.tick(60)

main()

pygbagに対応させるには、main関数をasync defに変更し、ループの末尾にawait asyncio.sleep(0)を一行加えます。これはブラウザのイベントループに制御を返すための記述で、この一行がないとブラウザが応答しなくなります。

python
import asyncio
import pygame

async def main():
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        screen.fill((0, 0, 0))
        pygame.display.flip()
        clock.tick(60)
        await asyncio.sleep(0)  # ←この一行が必須

asyncio.run(main())

ゲームの構造が複数ファイルに分かれている場合でも、エントリーポイントとなるmain.pyのトップレベルの呼び出しをこの形にすれば、他のファイルはasync化しなくても多くの場合は動きます。ただし、ゲーム中に別の重い処理をブロッキングで呼び出している箇所があると、そこで固まることがあります。そのような処理はawait asyncio.sleep(0)を挟むか、コルーチンに分割することを検討してください。

pygbagをインストールして変換を実行する

環境が整ったら、実際に変換します。手順は次のとおりです。

  1. pygbagをインストールする
   pip install pygbag
  1. プロジェクトの構成を確認する

pygbagはプロジェクトのディレクトリを渡す形で動きます。エントリーポイントのファイル名はmain.pyである必要があります。画像・音声・フォントなどのアセットはすべてmain.pyと同じディレクトリ以下に置き、ファイルパスの指定は相対パスにしておきます。絶対パスやos.pathで環境依存のパスを組み立てている箇所があると、ブラウザ上でファイルが見つからないエラーになります。

  1. 変換コマンドを実行する

ターミナルでプロジェクトの親ディレクトリに移動し、以下を実行します。

   python -m pygbag mygame/

mygame/の部分はプロジェクトのディレクトリ名に合わせてください。変換が始まると、ローカルにWebサーバーが立ち上がり、ブラウザで動作確認できるURLが表示されます。デフォルトではhttp://localhost:8000付近のアドレスになります。

  1. ブラウザで動作を確認する

表示されたURLをブラウザで開き、ゲームが正しく動くかを確認します。コンソールにエラーが出ている場合は、ブラウザの開発者ツール(F12キー)でメッセージを確認すると原因を特定しやすくなります。

  1. ビルド済みファイルを取り出す

正常に動作したら、プロジェクトディレクトリの中にbuild/webというフォルダが生成されています。この中にHTMLとアセット一式が入っています。投稿先のプラットフォームがZIPを受け付ける場合は、webフォルダをまるごとZIPに圧縮して提出します。

よく詰まるポイントと対処法

変換自体は成功してもブラウザ上で動かないケースがいくつかあります。遭遇しやすいものをまとめます。

音が出ない・途切れるブラウザはユーザー操作(クリックやキー入力)が発生するまで音声を再生しない制限を設けていることがあります。ゲーム起動直後に自動で音楽を流す処理を書いている場合、最初のキー入力やクリック後に鳴るよう変更すると解決するケースが多いです。

画像・フォントが読み込まれないブラウザ環境ではファイルの読み込みが非同期になることがあります。pygbagはasyncioベースのファイルロードヘルパーを提供しているため、アセットの読み込みにそれを使うと安定します。また、ファイル名の大文字・小文字がLinux系の環境で区別されるため、コード内の指定と実際のファイル名が一致しているかも確認してください。

importでエラーになるPyodideがサポートしていないC拡張ライブラリを使っていると、インポート時点で止まります。使用しているライブラリがPyodideの対応リストに含まれているかを事前に確認するか、同等の機能をPure Pythonで代替することを検討してください。

ローカルでは動くがデプロイ後に動かないbuild/webフォルダをそのまま静的ホスティングに置く場合、サーバーがWebAssemblyの配信に必要なMIMEタイプ(application/wasm)を正しく返さないと動きません。アソバースのようにゲームファイルの配信を前提として設計されたプラットフォームに出す場合は、この設定を気にする必要はありません。

変換後のゲームを公開する

build/webの中身をZIPにまとめれば、アソバースへの投稿形式に合致します。アソバースはHTMLファイルやZIPファイルの直接投稿に対応しているため、変換済みのファイルをそのままアップロードして公開できます。公開後はURLを共有するだけで、相手はPythonの環境なしでブラウザからプレイできます。

注意点として、ゲーム内のセーブデータやスコアをブラウザのLocalStorageに保存する処理を自前で実装している場合、その仕組みは引き続き動きます。ただし、アソバースのクラウドセーブ機能を利用するには作者側がSDKで対応する必要があり、pygbagの変換だけでは自動的には連携しません。

次の行動

手元にPygameプロジェクトがある場合は、まずメインループをasync defに書き換えてローカルでpygbagの変換を試してみてください。動作が確認できたらbuild/webフォルダをZIPにまとめ、アソバースに投稿することでブラウザからプレイできる状態で公開できます。pygbagのバージョンと対応するPythonのバージョンの組み合わせは変わることがあるため、作業前に公式ドキュメントで最新の対応状況を確認してください。

参考資料

  • pygbag 公式ドキュメント(pygbag GitHub リポジトリ)
  • Pyodide 公式ドキュメント(対応パッケージ一覧)
  • Pygame-ce 公式サイト