フェーズ 1 ウィンドウを出す

step0:星空を飛ぶ宇宙船

トップページstep0 概要 / フェーズ 1

ゴール

160×120 ドットの黒いウィンドウが開き、タイトルバーに Star Voyager と 表示される。Esc キーで閉じられる。ここまでを 11 行で作ります。

まだ何も動きません 真っ黒な画面が出るだけです。地味に見えますが、ここで作る「骨格」は step1 以降のゲームでもまったく同じ形で使い回します。すべての土台になる部分です。

1. 考え方

Pyxel のアプリは、次の 3 つの部品を用意して Pyxel に渡す、という形で作ります。 自分で「ずっと繰り返す」ループを書く必要はありません。繰り返しは Pyxel 側が面倒を見てくれます。

部品役割呼ばれる回数
画面の準備ウィンドウの大きさとタイトルを決める最初に 1 回だけ
更新(update座標を変える・キー入力を調べる。描画はしない1 秒間に 30 回
描画(draw画面を描き直す。状態は変えない1 秒間に 30 回

このフェーズでは update にはまだ何もさせず、draw画面を黒く塗りつぶすだけ にします。

なぜ毎フレーム塗りつぶすのか

Pyxel の画面は、前のフレームに描いた絵がそのまま残り続けます。 消さずに描き続けると、動くものが軌跡を引いて汚れていきます。 そこで毎フレームの描画のいちばん最初に画面全体を塗りつぶして、 まっさらな状態から描き直します。これが cls(clear screen=画面消去)です。

cls をしないと…前の絵が残る 1 フレーム目 → 2 → 3 (軌跡が残ってしまう) cls をすると…毎回まっさら 1 フレーム目 → 2 → 3 (きれいに動いて見える)
描画のたびに画面を消してから描き直す。これがアニメーションの基本形

2. このフェーズで使う API

API意味
pyxel.init(width, height, title) 画面を width×height ドットで用意する。title はウィンドウのタイトルバーに出る文字(省略時は "Pyxel"
pyxel.run(update, draw) ゲームループを開始する。以降 1 秒間に 30 回、updatedraw の順で呼び続ける
pyxel.cls(col) 画面全体を色番号 col で塗りつぶす。0 は黒
用語:API ライブラリが用意してくれている「呼び出せる命令」のこと。 pyxel.initpyxel.cls のように、pyxel. に続けて書きます。

3. コード

プロジェクト直下に step0 フォルダを作り、その中に star_voyager.py という名前で保存してください。

# star_voyager.py
# step0 フェーズ1:ウィンドウを出して、黒い画面を表示する

import pyxel


def update():
    """毎フレームの更新処理。このフェーズではまだ何もしない"""
    pass


def draw():
    """毎フレームの描画処理"""
    pyxel.cls(0)  # 画面全体を色番号 0(黒)で塗りつぶす


pyxel.init(160, 120, title="Star Voyager")  # 画面を用意する
pyxel.run(update, draw)                     # ゲームループを開始する

4. 実行する

VS Code のターミナルで、プロジェクト直下から次を実行します。

python step0/star_voyager.py

確認ポイント

4 つとも確認できたらフェーズ 1 は完了です。

5. コードの解説

import pyxel

Pyxel のモジュールを読み込みます。これ以降 pyxel.〇〇 の形で Pyxel の命令が使えるようになります。

def update():def draw():

更新用・描画用の関数を定義しています。ここではまだ定義しただけで、実行はされません。 実際に呼ぶのは Pyxel の役目です(後述の pyxel.run)。

"""毎フレームの更新処理。..."""

関数の直後に置いた文字列は docstring(説明文) と呼ばれ、 「この関数が何をするか」を書き残すためのものです。動作には影響しません。 コメント(#)と役割は似ていますが、docstring は関数の説明という決まった位置に置きます。

pass

「何もしない」ことを表す文です。Python では関数の中身を空っぽにするとエラーになるので、 中身がまだ無いことを明示するために置きます。フェーズ 3 で中身を書いたら消します。

pyxel.cls(0)

画面全体を色番号 0(黒)で塗りつぶします。Pyxel の色は #RRGGBB ではなく 0〜15 の番号で指定します。用意されているのは次の 16 色だけです。

0BLACK 1NAVY 2PURPLE 3GREEN 4BROWN 5DARK_BLUE 6LIGHT_BLUE 7WHITE 8RED 9ORANGE 10YELLOW 11LIME 12CYAN 13GRAY 14PINK 15PEACH
Pyxel 2.9.9 の標準パレット。pyxel.COLOR_YELLOW のように名前でも書けます(10 と同じ意味)

pyxel.init(160, 120, title="Star Voyager")

画面を横 160 ドット・縦 120 ドットで用意します。実際のウィンドウは 画面の大きさに合わせて自動で何倍かに拡大表示されます。

title="Star Voyager" のように 名前を書いて渡す 書き方を キーワード引数と呼びます。init には title のほかにも fps など省略可能な引数がいくつもあり、順番で渡すと分かりにくいため、 名前で指定します。省略した引数は既定値(fps なら 30)が使われます。

pyxel.run(update, draw) — このフェーズいちばんの新概念

update() ではなく update と書く(カッコを付けない) ここを pyxel.run(update(), draw()) と書くとエラーになります。

Python では、関数名をカッコ付きで書くと「今すぐ実行して、その結果を使う」意味になります。 一方、カッコなしで書くと「その関数そのもの」を指します。 関数は数値や文字列と同じように変数として持ち運べるのです。

run(update(), draw()) 先に update を 1 回実行して、 その戻り値(None)を渡してしまう → Pyxel は呼ぶものが無くエラー run(update, draw) update という関数そのものを渡す Pyxel が毎フレーム呼んでくれる → 正しい
「実行結果を渡す」のか「関数を渡す」のか。カッコの有無だけで意味が変わります

pyxel.run渡された関数を Pyxel 側が毎フレーム呼ぶための仕組みなので、 関数そのもの(カッコなし)を渡します。
なお pyxel.runそこで止まったままになり、ウィンドウを閉じるまで 次の行には進みません。だから run はいつもいちばん最後に書きます。

6. 試してみよう

コードを少し書き換えて、動きが変わることを確かめてみてください。

変える場所変えてみる値どうなるか
pyxel.cls(0)pyxel.cls(1)pyxel.cls(8)画面の色が変わる(上のパレット図と見比べる)
pyxel.init(160, 120, ...)pyxel.init(256, 256, ...)ウィンドウの大きさと形が変わる
title="Star Voyager"好きな文字列タイトルバーの表示が変わる

確認できたら、値は元に戻しておいてください(160, 120cls(0))。

7. うまくいかないときは

症状原因と対処
ModuleNotFoundError: No module named 'pyxel' Pyxel が入っていないか、別の Python から実行している。pip install pyxel を実行する
can't open file ...star_voyager.py 実行しているフォルダが違う。VS Code のターミナルがプロジェクト直下(learning_pyxel)にいるか確認する
IndentationError 関数の中身のインデント(字下げ)がずれている。def の次の行は半角スペース 4 つ下げる
ウィンドウが一瞬で閉じる pyxel.run(update, draw) の行が抜けている可能性が高い
ウィンドウが固まって閉じない ターミナルで Ctrl + C を押す
次のフェーズ フェーズ 2 では、この黒い画面に pyxel.text でタイトル文字を表示します。