step0:星空を飛ぶ宇宙船(最終フェーズ)
完成した Star Voyager を、Python も Pyxel も入っていない人のブラウザで遊べる形にして、 GitHub Pages で公開します。URL を渡すだけで人に遊んでもらえる状態がゴールです。
pyxel package で、作品を 1 つのファイルにまとめるpyxel app2html で、それを HTML 1 枚に変換するdocs/ に置いて GitHub Pages で公開する
いま step0 フォルダには、次の 2 つのファイルが入っています。
step0/
├── star_voyager.py プログラム本体
└── assets.pyxres 絵と音のファイル
この 2 つを友達に渡しても、たいていは動きません。相手のパソコンに
Python が入っていて、さらに pip install pyxel も済んでいて、
2 つのファイルを同じフォルダに置いてもらう必要があるからです。
「ちょっと見て」と気軽に渡せる状態ではありません。
そこで Pyxel には、この問題を解く 2 つのコマンドが用意されています。
フォルダ → 1 ファイル → HTML という順に変換します。1 回でやらず 2 段階に
分かれているのは、途中の .pyxapp だけでも配布に使えるからです(後述)。
ここが不思議なところです。ブラウザが動かせるのは本来 JavaScript だけのはずなのに、 Python で書いた Pyxel のゲームがなぜ動くのでしょうか。
つまり app2html が作る HTML は、Python を JavaScript に翻訳しているのではありません。
「ネット上に置いてある Pyxel 一式(wasm)を読み込んで、そこに自分の作品を渡して動かして」と
書いてあるだけです。作品のコードは Python のまま運ばれます。
実際に生成される HTML は、これで全部です。改行を除けば 4 行しかありません。
<!doctype html>
<script src="https://cdn.jsdelivr.net/gh/kitao/pyxel@2.9.9/wasm/pyxel.js"></script>
<script>
launchPyxel({ command: "play", name: "step0.pyxapp", gamepad: "enabled", base64: "UEsDBBQAAAAI..." });
</script>
読み方は次のとおりです。
| 部分 | 意味 |
|---|---|
<script src="https://cdn.jsdelivr.net/..."> |
Pyxel 一式(wasm)をネットから読み込む。
URL に pyxel@2.9.9 と書かれているとおり、
作ったときの Pyxel のバージョンが埋め込まれる |
base64: "UEsDBBQAAAAI..." |
作品(.pyxapp)そのもの。ZIP ファイルを文字列に変換して、 HTML の中に丸ごと埋め込んである |
command: "play" |
埋め込まれた作品を再生せよ、という指示 |
gamepad: "enabled" |
ゲームパッドがつながっていれば使えるようにする |
.pyxapp を一緒に置く必要はありません。
公開作業は「この HTML を 1 枚アップロードするだけ」で済みます。
このフェーズで使うのは、次の 3 つです。すべて pyxel コマンドで、
Python のコードの中に書くものではありません。ターミナル(VS Code のターミナル)で打ちます。
| コマンド | やること |
|---|---|
pyxel package APP_DIR SCRIPT.py |
APP_DIR フォルダの中身をまとめて APP_DIR.pyxapp を作る。
SCRIPT.py は「起動時に実行するファイル」の指定 |
pyxel play FILE.pyxapp |
できた .pyxapp をそのまま実行する。中身の確認用 |
pyxel app2html FILE.pyxapp |
.pyxapp から FILE.html を作る |
pyxel とだけ打つと、使えるコマンドの一覧が出ます。
忘れたらこれで確認できます。
まず、ターミナルの現在地をリポジトリのルート(step0 フォルダの
1 つ上)にします。VS Code でこのプロジェクトを開いていれば、
ターミナルは最初からルートにいるはずです。
pyxel package step0 step0/star_voyager.py
成功すると、次の 3 行が表示されます。
added 'step0\.pyxapp_startup_script'
added 'step0\assets.pyxres'
added 'step0\star_voyager.py'
そしてルートに step0.pyxapp ができます。
step0 フォルダの中でのファイル名」ではなく、
いまいる場所(ルート)から見たパスで書きます。
pyxel package step0 star_voyager.py ← ✕ no such file: 'star_voyager.py'
pyxel package step0 step0/star_voyager.py ← ○
step0 を 2 回書くので重複しているように見えますが、これが正しい書き方です。
HTML にする前に、この段階でちゃんと動くかを確かめます。
pyxel play step0.pyxapp
いつもどおりのウィンドウが開き、星が流れ、宇宙船が矢印キーで動き、BGM が鳴れば成功です。
Esc で閉じます。ここで動かないものは HTML にしても動きません。
.pyxapp は正体が ZIP なので、拡張子を .zip に変えれば中を覗けます
(備忘録の .pyxres の話と同じ仕組みです)。中身はこうなっています。
step0/.pyxapp_startup_script 中身は「star_voyager.py」の 1 行だけ
step0/assets.pyxres
step0/star_voyager.py
つまり pyxel package がやっているのは「フォルダを ZIP にして、
どれを最初に実行するかを書いたメモを 1 枚入れる」だけです。
pyxel app2html step0.pyxapp
何も表示されませんが、ルートに step0.html ができています。
サイズは 4 KB ほどしかありません。
できた HTML を、公開する前に手元のブラウザで確かめます。ここで ファイルをダブルクリックして開くのはおすすめしません。 ブラウザには「パソコンの中のファイルを直接開いたページ」に強い制限をかける 仕組みがあり、環境によっては真っ黒なまま動かないためです。
確実なのは、その場に簡易的な Web サーバーを立てて、そこから開く方法です。 Python に最初から付いている機能で立てられます。ターミナルで次を打ちます。
python -m http.server 8765
Serving HTTP on ... と表示されたら、ブラウザで次の URL を開きます。
http://127.0.0.1:8765/step0.html
すると、黒い画面の中央に CLICK TO START と出ます。
クリックすると、いつもの Star Voyager が始まります。
確認が済んだら、ターミナルで Ctrl + C を押してサーバーを止めます。
| 項目 | pyxel run(手元) | ブラウザ |
|---|---|---|
| 起動 | すぐ始まる | CLICK TO START のクリックが要る |
| キー入力 | すぐ効く | 画面をクリックして選択してからでないと効かない |
Esc | 終了する | 終了しない(タブを閉じる) |
| 起動の速さ | すぐ | 初回は wasm の読み込みで数秒かかる |
| ネット接続 | 不要 | 必要(Pyxel 本体を CDN から読み込むため) |
このリポジトリでは、docs/ フォルダの中身がそのまま Web に公開される設定に
なっています(仕組みは次の章で説明します)。作品の HTML も、その中に置きます。
置き場所は解説ページと同じ docs/site/step0/ にして、名前を作品名に変えます。
step0.html のままだと、URL を見ても何のページか分からないためです。
docs/site/step0/star_voyager.html
移動は VS Code のエクスプローラ(左側のファイル一覧)でドラッグ&ドロップし、
F2 で名前を変えるのが簡単です。ターミナルでやるなら次のとおりです。
Move-Item step0.html docs\site\step0\star_voyager.html
ルートに残った step0.pyxapp は、いつでも作り直せる中間ファイルです。
こういうものはリポジトリに入れません。.gitignore に 1 行足します。
# pyxel package が作る中間ファイル(いつでも作り直せるので追跡しない)
*.pyxapp
.pyxapp は .py と .pyxres から自動で作られるものです。
これを入れてしまうと、コードを直すたびに 中身が同じか違うか分からない古いファイルが
リポジトリに残り続けます。「元になるものだけを管理し、作られるものは管理しない」が
Git の基本的な考え方です。
star_voyager.html も生成物なのに、なぜ入れるのか。
それはこれが公開物そのものだからです。GitHub Pages は
リポジトリに置かれたファイルをそのまま配るだけなので、これは置く必要があります。
HTML を置いただけでは、URL を知っている人しかたどり着けません。 解説サイトから行けるようにします。2 か所に追記します。
① docs/site/step0/index.html(step0 概要ページ)の「完成イメージ」の下
<p style="text-align:center">
<a href="star_voyager.html">▶ 完成した作品をブラウザで遊ぶ</a>
</p>
② docs/site/index.html(トップページ)の step0 カードの中
カードの説明文のあとに、遊べることが分かる 1 行を足します。
さらに、リポジトリの README.md にも公開 URL を書いておくと、
GitHub をのぞいた人がすぐ遊べます。
star_voyager.html と index.html は同じフォルダにあるので、
リンクはファイル名だけで書けます(href="star_voyager.html")。
../ や https:// から始める必要はありません。
ここまでの変更を GitHub に送ります。VS Code のソース管理(左の枝分かれアイコン)を開くと、 変更されたファイルが一覧で出ます。
star_voyager.html、.gitignore、リンクを足した HTML が並んでいることを確認するstep0.pyxapp が一覧に出ていないことを確認する(出ていたら .gitignore の書き方を見直す)step0 フェーズ8:作品をパッケージ化して Web 公開 と入力プッシュしてから 数十秒〜数分待つと、次の URL で遊べるようになります。
https://mrgarita.github.io/learning_pyxel/site/step0/star_voyager.html
Ctrl + F5 で強制的に読み直せます。
GitHub Pages は、リポジトリの中の HTML をそのまま Web サイトとして配ってくれる GitHub の無料機能です。サーバーを借りる必要も、アップロード作業も要りません。 push すれば公開されるのが最大の利点です。
このリポジトリではすでに設定済みなので、今回は変更する必要はありません。 確認するには GitHub のリポジトリページで Settings → Pages を開きます。
| 項目 | 設定値 | 意味 |
|---|---|---|
| Source | Deploy from a branch | ブランチの中身をそのまま配る方式 |
| Branch | main | main ブランチの内容を公開する |
| Folder | /docs | docs フォルダの中だけを公開する |
step0/star_voyager.py や
CLAUDE.md まで Web からたどれる URL を持ってしまいます。
「見せたいものだけを 1 つのフォルダに集める」ほうが、
うっかり公開してしまう事故を防げます。
| 症状 | 原因と対処 |
|---|---|
no such file: 'star_voyager.py' |
2 番目の引数をいまいる場所からのパスで書く。
pyxel package step0 step0/star_voyager.py |
pyxel コマンドが見つからない |
pip install pyxel が済んでいるか確認する。
仮想環境を使っているなら、それを有効にしてから実行する。
python -m pyxel package ... のように
python -m を付けても実行できる |
pyxel play では動くのに、
ブラウザでは真っ黒のまま |
① HTML をダブルクリックで開いていないか。
python -m http.server 経由か、公開後の URL で開く。② ネットにつながっているか。Pyxel 本体を CDN から読み込むため、 オフラインでは動かない |
| CLICK TO START が出ない/ クリックしても始まらない | wasm の読み込みに数秒かかる。少し待つ。
それでも変わらなければ、ブラウザの開発者ツール(F12)の
Console タブに赤いエラーが出ていないか見る |
| 矢印キーを押しても宇宙船が動かない | ゲーム画面を一度クリックする。ブラウザは、選択されている場所にだけ キー入力を送るため |
| 音が鳴らない | CLICK TO START をクリックする前は鳴らない仕様。 クリック後も鳴らないなら、ブラウザのタブがミュートになっていないか確認する (タブのスピーカーアイコン) |
Esc で終了しない |
ブラウザでは終了できない(仕様)。タブを閉じる。
画面の PRESS ESC TO QUIT の表示は、手元で実行したとき向けの案内 |
| 公開 URL が 404 | ① push から数分待つ。② ファイルが本当に docs/ の中にあるか。
③ URL の綴りとフォルダ階層が一致しているか。
④ GitHub のリポジトリページでファイルが見えているか
(見えなければ push できていない) |
| 更新したのに古い内容が出る | ブラウザが前の内容を覚えている。Ctrl + F5 で強制再読み込み |
step0.pyxapp がコミット一覧に出てくる |
.gitignore に *.pyxapp を書いたか確認する。
すでに一度コミットしてしまった場合は .gitignore だけでは消えないので、
git rm --cached step0.pyxapp で追跡から外す |
| Pyxel を新しくしたら ブラウザ版だけ動きが変わった/動かない | HTML には作ったときのバージョン(pyxel@2.9.9)が
埋め込まれている。Pyxel を更新したら
package からやり直して HTML を作り直す |
pyxel.load("assets.pyxres") のパスは大丈夫?
フェーズ 5 で「pyxel.load の相対パスは実行するスクリプトのある場所を基準に
解決される」と学びました。パッケージ化しても同じです。
.pyxapp の中で star_voyager.py と assets.pyxres は
同じ step0/ フォルダに並んだまま収められるので、コードは 1 文字も直さずに動きます。
これで step0 は完了です。8 つのフェーズで、次のことを一通り通りました。
| フェーズ | 身についたこと |
|---|---|
| 1 | Pyxel の骨格(init → run、update と draw の役割分担) |
| 2 | 画面座標系と 16 色パレット、文字の描画 |
| 3 | リストで多数のものを管理し、毎フレーム動かすアニメーションの考え方 |
| 4 | リソースエディタでのドット絵制作、コードをクラスにまとめる設計 |
| 5 | スプライトの転送と透過色、リソースファイルの読み込み |
| 6 | キー入力の判定と、画面外に出さないための値の制限 |
| 7 | サウンドとミュージックの作成、BGM のループ再生 |
| 8 | 作品のパッケージ化と Web 公開、生成物と原本の区別 |
ここまでで「画面に出す・動かす・操作する・音を鳴らす・人に届ける」という、 ゲーム制作の一周分がそろいました。step1 からは、これらを組み合わせて ルールのあるゲームを作っていきます。