step0:星空を飛ぶ宇宙船
黒い画面の中央上部に STAR VOYAGER が黄色で表示され、
その下に灰色で操作説明の文字が出る。どちらも画面の横中央にきちんと揃っている。
Pyxel では文字も図形と同じ描画です。したがって書く場所は draw の中、
そして pyxel.cls より後です。cls は画面全体を塗りつぶすので、
先に文字を描いてから cls すると塗りつぶされて消えてしまいます。
draw の中の命令の順番は、そのまま画面の重なり順になります。
pyxel.text(x, y, s, col) の x, y は、文字列の左上の角の位置です。
中心でも左下でもありません。そして Pyxel の内蔵フォントは
1 文字がぴったり 4×6 ドットと決まっています。
「中央に置く」は、Pyxel が用意してくれてはいません。自分で x 座標を計算します。 考え方は単純で、画面幅から文字列の幅を引いて、余った左右の余白を半分ずつに分けるだけです。
| API・定数 | 意味 |
|---|---|
pyxel.text(x, y, s, col) | 文字列 s を色 col で、左上が (x, y) になるように描く |
pyxel.FONT_WIDTH | 内蔵フォント 1 文字の幅。中身は 4 |
pyxel.FONT_HEIGHT | 内蔵フォント 1 文字の高さ。中身は 6 |
pyxel.COLOR_YELLOW ほか | 色番号に付けられた名前。COLOR_YELLOW は 10 と同じ意味 |
step0/star_voyager.py を、次の内容に書き換えてください。
# star_voyager.py
# step0 フェーズ2:タイトル文字を表示する
import pyxel
SCREEN_WIDTH = 160 # 画面の横幅(ドット)
SCREEN_HEIGHT = 120 # 画面の縦幅(ドット)
TITLE = "STAR VOYAGER"
GUIDE = "PRESS ESC TO QUIT"
def text_center(y, s, col):
"""文字列 s を、画面の横中央に来るように描く"""
x = (SCREEN_WIDTH - len(s) * pyxel.FONT_WIDTH) // 2
pyxel.text(x, y, s, col)
def update():
"""毎フレームの更新処理。このフェーズではまだ何もしない"""
pass
def draw():
"""毎フレームの描画処理"""
pyxel.cls(0) # 画面を黒で塗りつぶす
text_center(20, TITLE, pyxel.COLOR_YELLOW) # タイトル
text_center(34, GUIDE, pyxel.COLOR_GRAY) # 操作説明
pyxel.init(SCREEN_WIDTH, SCREEN_HEIGHT, title="Star Voyager")
pyxel.run(update, draw)
python step0/star_voyager.py
STAR VOYAGER が黄色で表示されるPRESS ESC TO QUIT が灰色で表示されるEsc で閉じられるSCREEN_WIDTH = 160
TITLE = "STAR VOYAGER"
Python には「変更できない値」を作る仕組みはありませんが、 全部大文字の名前を付けたものは途中で書き換えないという約束(慣習)があります。 これを定数と呼びます。
理由は 2 つあります。
160 という数字を init の中と中央揃えの計算の
両方に直接書くと、画面幅を変えたいときに両方を直す必要があります。書き換え漏れはバグの定番です(160 - ...) より (SCREEN_WIDTH - ...) のほうが、
何を計算しているか一目で分かりますdef text_center(y, s, col):
x = (SCREEN_WIDTH - len(s) * pyxel.FONT_WIDTH) // 2
pyxel.text(x, y, s, col)
フェーズ 1 の update や draw はカッコの中が空でしたが、
今回は y, s, col の 3 つを受け取ります。これを引数と呼びます。
呼ぶ側が値を渡し、関数の中ではその値が変数として使えます。
なぜ関数にまとめるのか。 中央揃えの計算は、タイトルと操作説明の 2 か所で必要です。 同じ計算を 2 回書くと、片方だけ直して食い違うという事故が起きます。 1 か所にまとめておけば、直すときも 1 か所で済みます。
text_center(y, s, col) の順番は、pyxel.text(x, y, s, col) から
x を取り除いた並びに合わせています。元の API と似た並びにしておくと混乱しません。
len(s)
文字列 s の文字数を返す Python の組み込み関数です。
len("STAR") なら 4。リストに使えば要素の個数を返します。
// — 整数の割り算Python の割り算には 2 種類あり、この違いは Pyxel では特に重要です。
| 書き方 | 結果 | 型 |
|---|---|---|
112 / 2 | 56.0 | 小数(float) |
112 // 2 | 56 | 整数(int) |
113 // 2 | 56 | 整数(小数点以下は切り捨て) |
/ は割り切れても必ず小数になります。画面のドットは整数のマス目なので、
座標の計算には // を使って整数のまま扱うほうが素直です。
/ を使ってもエラーにはなりません
Pyxel の座標は小数も受け付けます(内部で切り捨てられます)。
ただし print で座標を確認したときに 56.0 と表示されて紛らわしいので、
整数で扱えるところは整数にしておきます。
pyxel.COLOR_YELLOW
10 と書いても同じですが、名前で書くほうが読んだときに色が分かります。
16 色すべてに名前が付いています。
BLACK(0) NAVY(1) PURPLE(2) GREEN(3)
BROWN(4) DARK_BLUE(5) LIGHT_BLUE(6) WHITE(7)
RED(8) ORANGE(9) YELLOW(10) LIME(11)
CYAN(12) GRAY(13) PINK(14) PEACH(15)
COLOR_GREEN(3) は青緑、COLOR_CYAN(12) は青紫寄り、
COLOR_RED(8) は赤紫です。名前を鵜呑みにせず、
フェーズ 1 のパレット図で実際の色を確かめてください。
文字の高さは 6 ドット(pyxel.FONT_HEIGHT)です。
20 と 34 の差は 14 なので、文字と文字の間に 8 ドットのすき間ができます。
数字に深い意味はなく、見た目のバランスで決めています。好きに変えてかまいません。
| やってみること | 確かめたいこと |
|---|---|
TITLE の文字列を長さの違うものに変える |
文字数が変わっても、自動で中央に揃い続ける |
draw の中で pyxel.cls(0) をいちばん最後に移動する |
文字が塗りつぶされて何も見えなくなる(=順番が重要な理由) |
text_center の // 2 を // 4 に変える |
文字が左寄りになる。式の意味を実感できる |
色を pyxel.COLOR_GREEN や pyxel.COLOR_RED に変える |
名前から想像する色と実際の色が違うことを確認できる |
確認できたら、コードは元に戻しておいてください。
| 症状 | 原因と対処 |
|---|---|
| 何も表示されない(真っ黒のまま) | pyxel.cls(0) が text_center より後ろにある。cls はいちばん先頭に置く |
NameError: name 'text_center' is not defined |
def text_center の定義が draw より後ろにある、または名前のつづりが違う |
TypeError: text_center() missing 1 required positional argument |
呼び出し時の引数が足りない。text_center(20, TITLE, pyxel.COLOR_YELLOW) と 3 つ渡す |
| 文字が中央からずれる | SCREEN_WIDTH と pyxel.init に渡した幅が食い違っている |
TabError: inconsistent use of tabs and spaces |
タブとスペースが混ざっている。備忘録の該当項目を参照 |
update に中身が入り、画面が「動き」ます。