Pygame Zero へようこそ

Pygame Zero を使うと決まりきった手続きをいちいち書かずにゲームを作ることができます。

Pygame Zero は教育の場での利用を意図しており、指導者は Pygame の API やイベントループの説明をしなくても、基本的なプログラミングを教えられます。

学習コース

Pygame Zero 入門

ウィンドウの作成

まず intro.py という名前の中身が空のファイルを作ります。

次のように実行すると、ウィンドウだけが表示されることを確認しましょう

pgzrun intro.py

Pygame Zero はプログラムを何も書かなくても動くようになっています。空のファイルも Pygame Zero の世界ではれっきとしたプログラムです!

ウィンドウの閉じるボタンをクリック、またはキーボードの Ctrl-Q (Macなら ⌘-Q) で終了します。何か問題があってゲームが応答しなくなったときは、ターミナルで Ctrl-C を押すと終了します。

背景の描画

次は draw() という関数を作成し、ウィンドウのサイズを指定してみましょう。Pygame Zero は画面の描画が必要なとき、自動的にこの関数を呼び出すようになっています。

ファイル intro.py に以下の内容を記述します。

1
2
3
4
5
WIDTH = 300
HEIGHT = 300

def draw():
    screen.fill((128, 0, 0))

もう一度 pgzrun intro.py を実行してみると、今度は赤っぽい正方形が表示されます!

このコードは何をやっているのでしょう?

WIDTHHEIGHT はそれぞれウィンドウの高さと幅を指定するものです。このコードではウィンドウのサイズとして縦横それぞれに300ピクセルを指定しています。

screen はウィンドウのスクリーンを表わす組込みのオブジェクトで スプライトやシェイプを描画するさまざまなメソッドを備えています

screen.fill() メソッドは (red, green, blue) 形式のタプルで色を指定して呼び出すことにより、スクリーンをその色で塗りつぶします。たとえは (128, 0, 0) を指定するとやや暗めの赤になります。数値を0から255の間で変えてみて、どのような色になるか確認してみましょう。

次はアニメーションを実現するスプライトを設定します。

スプライトの描画

表示処理を書く前にまず、エイリアンのスプライトを使用できるようディスクに保存しておきましょう。次の画像を右クリックで保存してください(「名前を付けて画像を保存」などを選択します)。

_images/alien.png

(このスプライトは、ゲームに必要な画像透明表示を行うアルファ・チャンネルを備えています。ただし暗い背景で使うことを前提にしたものなので、エイリアンの宇宙服のヘルメット部分は、実際にゲーム内で表示するまでよく見えないかもしれません。)

ちなみに

このエイリアンをはじめ、たくさんの無料スプライトが kenney.nl で入手できます。 エイリアンは Platformer Art Deluxe pack に収録されています。

保存したファイルは Pygame Zero が探し出せる場所に置きます。 images という名前のディレクトリを作成し、その中に alien.png を保存します。ディレクトリ名とファイル名はどちらもアルファベットの小文字だけを使ってください。そうしないと、Pygame Zero はクロスプラットフォームの互換性に問題が発生する旨の警告を発します。

ファイルを保存した段階で、プロジェクトのファイル配置は次のようになっているはずです。

.
├── images/
│   └── alien.png
└── intro.py

Pygame Zero は images/ は画像を探し出す場所として決められたディレクトリです。

Actor という組込みのクラスがあります。これを使うとスクリーンに画像を表示することができます。

では試してみましょう。 intro.py を次のように変更してください。

1
2
3
4
5
6
7
8
9
alien = Actor('alien')
alien.pos = 100, 56

WIDTH = 500
HEIGHT = alien.height + 20

def draw():
    screen.clear()
    alien.draw()

エイリアンがスクリーンに表示されました! Actor クラスに 'alien' という文字列を指定すると、自動的にスプライトを読み込み、位置やサイズなどの属性がセットされます。 ここではエイリアンの高さの属性を利用してさらにスクリーンの高さ HEIGHT を設定しています。

alien.draw() メソッドはスクリーン上の現在の位置にスプライトを描画します。

エイリアンを動かす

エイリアンを一旦スクリーンの外に出します。 alien.pos の行を次のように変更してください。

alien.topright = 0, 10

topright への値のセットはエイリアンの右上の座標で位置指定することを意味します。エイリアン右上の x 座標は 0 ですから結果的にエイリアンはスクリーンの外に配置され表示されません。続いてこれを動かしてみましょう。ファイルの末尾に次のコードを追加してください。

def update():
    alien.left += 2
    if alien.left > WIDTH:
        alien.right = 0

Pygame Zero はフレーム描画のたびに、関数 update() を呼び出します。フレームごとに表示位置を数ピクセルずつ変えることで、アイリアンがスクリーンをすーっと横切っていくように見えます。エイリアンが右端に消えたところで座標をリセットし、ふたたび左端から現れるようにしています。

draw()update() は似ていますが用途が違います。 draw() がエイリアンの最初の位置を描画するのに対し、 update() はスクリーン上でエイリアンを動かして見せるために使います。

マウスのクリックを検知

今度はマウスでエイリアンをクリックしたとき何か起きるようにしてみましょう。この機能を実現する関数は on_mouse_down() です。これをソースコードに追加します。

1
2
3
4
5
def on_mouse_down(pos):
    if alien.collidepoint(pos):
        print("Eek!")
    else:
        print("You missed me!")

ゲームを起動してエイリアンをクリック、またはエイリアンがいないところをクリックしてみてください。

Pygame Zero は関数の呼び出し方を自動的に判別するようになっています。関数を pos パラメータ無しで定義すると、Pygame Zero はパラメータを使わずに関数を呼び出します。 on_mouse_down には同じように省略可能なパラメータ button があります。たとえばパラメータを使わないで次のようにも書けます。

def on_mouse_down():
    print("You clicked!")

パラメータを使う場合の例はこうなります。

def on_mouse_down(pos, button):
    if button == mouse.LEFT and alien.collidepoint(pos):
        print("Eek!")

サウンドとイメージ

それでは次にエイリアンが倒れるようにしてみましょう。次のファイルを保存してください。

  • alien_hurt.png - alien_hurt.png という名前で images ディレクトリに保存してください。
  • eep.wav - sounds という名前のディレクトリを新たに作り、 eep.wav という名前で保存してください。

プロジェクトのファイル構成は次のようになります。

.
├── images/
│   └── alien.png
│   └── alien_hurt.png
├── sounds/
│   └── eep.wav
└── intro.py

sounds/ はPygame Zero がサウンド・ファイルを探し出す場所として決められたディレクトリです。

on_mouse_down を変更してこれら新しく追加したファイルを使うようにします。

def on_mouse_down(pos):
    if alien.collidepoint(pos):
        alien.image = 'alien_hurt'
        sounds.eep.play()

これでエイリアンをクリックすると、音が鳴ってスプライトが倒れた姿のエイリアンに変わります。

ただしこのプログラムにはバグがあります。一度クリックすると、エイリアンは元の姿に戻りません(にもかかわらずクリックのたびに音はします)。この部分を修正しましょう。

クロック

ゲームプログラミング以外でPythonを使ったことがある人なら、一定時間処理を止めるのに time.sleep() を使うことを知っていて、次のようなコードを書こうとするかもしれません。

1
2
3
4
5
6
def on_mouse_down(pos):
    if alien.collidepoint(pos):
        alien.image = 'alien_hurt'
        sounds.eep.play()
        time.sleep(1)
        alien.image = 'alien'

残念ながらこのようなコードはゲーム向きではありません。 time.sleep() はプログラム内の処理をすべて止めてしまいます。ですがゲーム自体はそのまま進行し、アニメーションは動き続けてほしいのです。つまり on_mouse_down は処理を止めずにリターンして、通常処理の draw()update() を実行しながら、必要なときにだけエイリアンのリセット処理を行うようにしたいのです。

Pygame Zero でこのような処理は難しくありません。組込みの Clock を使えば時間をおいて後から実行する関数をスケジュールできるからです。

ではリファクタリング(プログラムコードの再構成)をしてみましょう。エイリアンを倒れた姿にする関数と、それを元の状態に戻す関数を作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def on_mouse_down(pos):
    if alien.collidepoint(pos):
        set_alien_hurt()


def set_alien_hurt():
    alien.image = 'alien_hurt'
    sounds.eep.play()


def set_alien_normal():
    alien.image = 'alien'

このままだと前のコードと実行結果は変わりありません。 set_alien_normal() が呼び出されていないからです。 set_alien_hurt() の中で clock を使い、少し時間がたってから set_alien_normal() を呼び出すように修正してみましょう。

def set_alien_hurt():
    alien.image = 'alien_hurt'
    sounds.eep.play()
    clock.schedule_unique(set_alien_normal, 0.5)

上記は clock.schedule_unique()set_alien_normal()0.5 秒後に呼び出すようにしています。また、早く何度もクリックされても schedule_unique() は同じ関数を同時にひとつしかスケジュールしないようになっています。

プログラムを動かして試してみると、エイリアンをクリックして倒しても、0.5秒後には元に戻るようになっているはずです。早く何度もクリックすることも試してみてください。一番最後のクリックから0.5秒後にエイリアンは元に戻るはずです。

clock.schedule_unique() で指定する実行までの秒数は整数、小数のどちらでも指定できます。この例では小数を使っていますが、整数と小数、値をいろいろ変えて動きがどのように変わるか試してみましょう。

まとめ

ここまででスプライトの読み込みと表示、音の再生、マウスの入力イベント処理、組込みの clock の使い方を学びました。

ゲームをさらに拡張してスコアを表示したり、エイリアンがもっと不規則な動き方をするようにしたいかもしれませんね。

Pygame Zero にはかんたんに使える組込みの機能がまだたくさんあります。 組込みオブジェクト を読んで、そのほかのAPIの使い方を学んでください。

Scratch からの移行

このチュートリアルでは、Scratch で作られた Flappy Bird と Pygame Zero で作られたものを比較してみます。注目していただきたいのは、Scratch と Pygame Zeroのプログラムがよく似ていることです。

Pygame Zero版 はPygame Zero のリポジトリにあります。

また Scratch版 も同じリポジトリからダウンロードできます。

Pygame Zero 版にはスクロール処理も含まれていますが、Scratch コードとの直接的な対比のため、このページのサンプルからはその部分を除いています。

またサンプルの Pythonコード は例をより分かりやすくするために再構成しています。

ステージ

Scratch 版のステージはこの図のようになっています。

_images/flappybird-stage.png

背景をのぞけば、オブジェクトは三つしかありません。鳥と上のパイプと下のパイプです。

Pygame Zero のプログラムではこの三つのオブジェクトをそれぞれ次のように Actor として定義します。

bird = Actor('bird1', (75, 200))
pipe_top = Actor('top', anchor=('left', 'bottom'))
pipe_bottom = Actor('bottom', anchor=('left', 'top'))

Pygame Zero ではさらにこれらのオブジェクトの描画を行う必要があります。一方これがシーンの描画処理の書き方に柔軟性を与えてくれます。

def draw():
    screen.blit('background', (0, 0))
    pipe_top.draw()
    pipe_bottom.draw()
    bird.draw()

パイプを動かす

2本のパイプは鳥の動きとは無関係に一定のスピードで動きます。パイプはスクリーン左側に消えると、右側から再び現れ、このときパイプの縦の位置はランダムに変わるようになっています。

Scratchでは、上下2本のパイプにそれぞれ異なるスクリプトを設定して、これを実現しています。

_images/flappybird-top-start.png _images/flappybird-bottom-start.png

このスクリプトの実行内容は次の通りです。 :

  • x position < -240 という条件が成り立つと、パイプがスクリーン左の見えなくなる位置まで来たことを意味します。これがパイプの表示をリセットする合図になります。
  • 変数 pipe_height は2本のパイプの位を置調整するのに使われます。しかしこの値を2つのスクリプトそれぞれでランダムな値をセットしてしまうと、パイプの間の長さを一定に保てません。このため値をセットするスクリプトは片方だけにしています。
  • set y position to pipe height +/- 230 の部分でプラスをセットしている方のパイプは pipe_height より上に、マイナス側は pipe_height の下に表示されます。

同じ処理を Pygame Zero で書いた場合、もっとシンプルになります。上下両パイプの描画更新処理をひとつの関数にまとめることもできるのですが、わかりやすいように更新とリセットの処理を分けてみました。

import random

WIDTH = 400
HEIGHT = 708
GAP = 130
SPEED = 3

def reset_pipes():
    pipe_gap_y = random.randint(200, HEIGHT - 200)
    pipe_top.pos = (WIDTH, pipe_gap_y - GAP // 2)
    pipe_bottom.pos = (WIDTH, pipe_gap_y + GAP // 2)

def update_pipes():
    pipe_top.left -= SPEED
    pipe_bottom.left -= SPEED
    if pipe_top.right < 0:
        reset_pipes()

ここで Scratch とやや違っているのは、値を再利用しやすいように、大文字の定数として定義している点です。こうすることで値の定義場所が一カ所にまとまりチューニングしやすくなります。たとえばこのコードだと、2つのパイプの間を広くしたり、狭くしたり変更したいときは GAP の値を変えるだけで済みます。

一方、大きく違う点は Python コードに forever (ずっと) のループが無いことです。ここが Scratch とテキストベースの大半のプログラミング言語との大きな違いです。ゲームの更新処理はひとつのアニメーションのステップごとに、すぐにリターンするように作らなければなりません。リターンすることで Pygame Zero は入力を処理したり、スクリーンの再描画など他の処理ができるようになります。ループが終了しないとゲームはその処理から抜けられなくなってしまうので、ほかのループを使う場合もすぐ抜け出るようにしておく必要があります。

Pygame Zero はアニメーション更新のステップの度、関数 update() を呼び出します。ですからこの関数の中で update_pipes() を呼び出すだけで OK です。

def update():
   update_pipes()

前の項で Scratch のロジックを Python コードに変換する方法を説明しました。続いて鳥を動かすロジックも変換してみましょう。今度は最初から Python コードの方を見てみましょう。

鳥の状態を更新するコードは update_bird() という関数内に記述しています。ここでまず行なっていることは、鳥を重力にしたがって動かすことです。

GRAVITY = 0.3

# 鳥の初期状態を設定
bird.dead = False
bird.vy = 0

def update_bird():
    uy = bird.vy
    bird.vy += GRAVITY
    bird.y += bird.vy
    bird.x = 75

以下は重力のシンプルな法則です。

  • 重力(Gravity)とは 下方向にはたらく一定の加速度(Acceleration) です。
  • 加速度は 速度(velocity) を変化させます。
  • 速度は 位置(position) を変化させます。

これを表現するために y 方向の速度を変数 bird.vy で保持します。これは新たに定義した変数で、Pygame Zero が元から提供しているものではありません。

  • 重力は下方向にはたらく一定の加速度です: つまり GRAVITY はゼロより大きい。
  • 加速度は速度を変化させます: GRAVITY から得た値が bird.vy に加算されます。
  • 速度は位置を変化させます: bird.vy から得た値が bird.y に加算されます。

鳥が水平方向には動かないことに注意してください! 鳥の x 座標はゲームを通じて75に固定しています。逆にパイプを鳥に向かって動くようにすることで、鳥が動いているように見せます。そうすることで動いている鳥をカメラが追っているような見た目になります。したがってこのゲームでは変数 vx を使う必要はありません。

さて、次は鳥を羽ばたかせてみます。

if not bird.dead:
    if bird.vy < -3:
        bird.image = 'bird2'
    else:
        bird.image = 'bird1'

このコードは鳥が上下どちらに向かっているかをチェックしています。もし上向きの早い速度なら bird2 の画像を表示、それ以外は bird1 を表示するようにしています(-3という値は実際に色々試して意図した通りの動きになる値を導き出しました)。

次の部分では鳥がパイプの壁に衝突していないかをチェックしています

if bird.colliderect(pipe_top) or bird.colliderect(pipe_bottom):
    bird.dead = True
    bird.image = 'birddead'

もし衝突していたら bird.deadTrue をセットします。これは ブール値 といって、値は TrueFalse の二通りです。この値をチェックすることで、鳥が生きているかどうかを調べられます。もし生きていなければ、プレイヤーの入力には反応しなくなります。

最後の部分では鳥がゲーム・スクリーンの一番下(または一番上)に逹していないかチェックしています。もし逹していたら鳥の位置をリセットします

if not 0 < bird.y < 720:
    bird.y = 200
    bird.dead = False
    bird.vy = 0
    reset_pipes()

ここで reset_pipes() を呼んでいるのはなぜでしょう?パイプのコードはこの独立した関数内に書かれているため、壁の状態をリセットしたいときは、これを呼び出すだけで済むようになっています。この場合、鳥が最初の状態にポジションに戻ると同時に、壁も初期化されるようにしているのです。

そして前にも説明した通り、これらの処理はフレーム毎に呼び出す必要があります。 update() に追加しましょう

def update():
   update_pipes()
   update_bird()

最後はプレイヤーの操作に応じて鳥を動かす処理です。何かのキーを押すと、鳥が羽ばたき上昇するようにします。Pygame Zero はキーが押されると on_key_down() を呼び出すようになっています

FLAP_VELOCITY = -6.5

def on_key_down():
    if not bird.dead:
        bird.vy = FLAP_VELOCITY

もし鳥が生きていたら、 vy にマイナスの値をセットするようにしています。これはPygame Zero の世界で、上へ向かって動かすことを意味します。

Python のコードと次の Scratch のコードをくらべてみると、似た点がたくさん見つかるはずです。

_images/flappybird-bird-start.png _images/flappybird-bird-space.png

Scratch と Pygame Zero で大きく違う点は次の通りです。

  • Pygame Zero では「ずっと」の繰り返しを使う必要はありません。フレーム毎に自動で実行される update() に更新内容を書いてリターンするだけです。
  • 座標の書き方が違います。Pygame Zero ではスクリーンの左上が x = 0, y = 0 になります。x は左から右に移動すると増えていきますが、 y は上から下に向かって増えていきます。Python のコードで GRAVITY はプラスの値、 FLAP_VELOCITY がマイナスの値となっているのはこのためです。
  • bird.dead はブール値です。このため Scratch で dead = 0 のように値を比較していたは if not bird.dead と書けます。

まとめ

Scratch コードでの書き方の多くは、Pygame Zero のコードに直接変換可能です。

いくつかの対応例を記載します。

Scratch コード Pygame Zeroコード
y 1 ずつ変える (上に移動) bird.y -= 1
y -1 ずつ変える (下に移動) bird.y += 1
コスチュームを <name> にする bird.image = 'name'
もし dead = 0 なら if not bird.dead:
dead 0 にする bird.dead = False
もし Top に触れたなら if bird.colliderect(pipe_top)
旗が押されたとき... ずっと 処理内容を関数 update() に記述する
どれかのキーが押されたとき def on_key_down():
a から b までの乱数 import randomrandom モジュールを ロードして random.randint(a, b) を実行する
(0, 0) はステージの中心 (0, 0) はスクリーンの左上隅

コードのいくつかの部分では Python の方がよりシンプルになっています。それはコードを読むときに理解しやすいような作りになっているためです。

また Pygame Zero の actor は座標の操作が簡単にできます。たとえば今回パイプの位置指定に anchor を使っています。これによってパイプがスクリーンの外に出たかどうかを if x position < -240 ではなく pipe_top.right < 0 と書けます。

リファレンス

イベントのフック

イベントのフック(検知)を定義すると Pygame Zero はそのイベントに対応する処理を呼び出してくれるようになっています。この仕組みにより、イベント・ループの仕組みをわざわざ自分で書く必要はありません。

ゲーム・ループのフック

通常、ゲームのプログラムでは次のようなゲーム・ループを書く必要があります

while game_has_not_ended():
    process_input()
    update()
    draw()

そしてもっと複雑な入力処理を書く必要があるのですが、Pygame Zero を使うと update()draw() という関数をゲームプログラムに定義するだけで済みます。

draw()

ゲームのスクリーンの再描画が必要なとき、Pygame Zero はこの関数を呼び出します。

draw() に引数はありません。

Pygame Zero は何も変更がなく、再描画の必要がなければ処理をスキップするようになっています。ゲーム・ループの繰り返しで、次の条件が当てはまるときだけスクリーンの再描画が行われます。

  • 関数 update() が定義されているとき(後述)。
  • クロック・イベントが発生したとき。
  • 入力イベントが発生したとき。

ここで注意してほしいのは、draw 関数で表示内容を変更したり動かしたりするときの書き方です。たとえば次のコードは間違っています。エイリアンをスクリーン上で横に動かしたいのですが、意図したようには動きません。

def draw():
    alien.left += 1
    alien.draw()

以下が正しいコードです。表示内容を変えたり動かしたり、あるいは単にスクリーンに色を塗りたいときは、 update() を使います

def draw():
    alien.draw()

def update():
    alien.left += 1
update() または update(dt)

Pygame Zero はゲームを進めるためにこの関数を呼び出します。この関数は1秒間に60回、くり返し呼び出されます。

update 関数はアプローチの異なる二通りの書き方があります。

簡単なゲームの場合、 update() 呼び出しから次の呼び出しまでの1ステップに必要な時間はわずか、ほんの一瞬とみなせます。おそらくその時間がどれくらいか気にすることもないでしょう。この場合1フレームで行う処理は、オブジェクトをある決まったピクセル数だけ動かしたり、あるいは決まった割合で加速させたり、などです。

より高度なアプローチは、呼び出しから呼び出しの間にかかった時間をベースに運動と物理の計算を行うことです。この方法を使うとアニメーションを滑らかに表示できる一方で、計算量が大きくなる可能性があり、またその計算に時間が長くかかったときに予想外の振舞いをしないよう注意しなければなりません。

時間ベースのアプローチを使用する場合は、update が一つの引数を受け取るようにします。関数updateに引数が設定されている場合、Pygame Zero は秒単位の経過時間を引数として渡します。この結果、その時間に応じた動きを計算をすることが可能になります。

イベント処理のフック

ゲームループのフックと同様に、Pygame Zero プログラムでは特定の名前で関数を定義することにより、入力イベントも処理することができます。

update() と同じように、Pygame Zero はイベントハンドラ関数を調べてその呼び出し方法を判別します。ですからイベントハンドラ関数の引数は必須ではありません。たとえば Pygame Zero は以下の on_mouse_down 関数のバリエーションの内、どれを使ってもちゃんと呼び出してくれます。

def on_mouse_down():
print("マウスのボタンがクリックされました")
def on_mouse_down(pos):
print("マウスのボタンが", pos, "の位置でクリックされました")
def on_mouse_down(button):
print("マウスの", button, "ボタンがクリックされました")
def on_mouse_down(pos, button):
print("マウスの", button, "ボタンが", pos, "の位置でクリックされました")

判別は引数の名前によって行なわれます。ですから引数を使う場合、名前は正確に入力する必要があります。それぞれのイベント・フックで使用できる引数の内訳を以下に記載します。

on_mouse_down([pos][, button])

マウスボタンが押されたときに呼び出されます。

パラメータ:
  • pos -- (x, y)形式のタプルで、ボタンが押されたときのマウス・ポインタの位置を示します。
  • button -- 列挙型 mouse の値で押されたボタンがどれかを示します。
on_mouse_up([pos][, button])

マウスボタンが離されたときに呼び出されます。

パラメータ:
  • pos -- (x, y)形式のタプルで、ボタンが離されたときのマウス・ポインタの位置を示します。
  • button -- 列挙型 mouse の値で離されたボタンがどれかを示します。
on_mouse_move([pos][, rel][, buttons])

マウスが動かされたときに呼び出されます。

パラメータ:
  • pos -- (x, y)形式のタプルで、動かした先のマウス・ポインタの位置を示します。
  • rel -- (delta_x, delta_y)形式のタプルで、マウス・ポインタの位置の変化量を示します。
  • buttons -- 列挙型 mouse の値の集合です。移動の間押されていたボタン(複数)を示します。

マウスのドラッグを処理したいときは、次のコードを参考にしてください

def on_mouse_move(rel, buttons):
    if mouse.LEFT in buttons:
        # マウスがドラッグされた。`rel` を使って続く処理を記述する
        ...
on_key_down([key][, mod][, unicode])

キーが押されたときに呼び出されます。

パラメータ:
  • key -- 整数で、押されたキーを示します(下記参照)。
  • unicode -- キーで入力された文字。ただし制御文字のように表示できない文字の場合もあります。キーに対応しているユニコードが無い場合は空文字列となります。
  • mod -- 押された修飾キーのビットマスク。
on_key_up([key][, mod])

キーが離されたときに呼び出されます。

パラメータ:
  • key -- 整数で、離されたキーを示します(下記参照)。
  • mod -- 押された修飾キーのビットマスク。
on_music_end()

音楽のトラック が完了したときに呼び出されます。

ただしトラックにループ設定がされている場合、この関数が呼び出されることはないので注意してください。

マウスのボタンとキー

組込みのオブジェクト mousekeys は前述のイベントでどのボタンやキーが押されたのかを示すために使われます。

マウスのスクロールホイールのイベントは次に記載したボタンの定数 WHEEL_UP または WHEEL_DOWN のボタン操作として扱われます。

class mouse

マウスのボタンを示す組込みの列挙型オブジェクトで、 on_mouse_* ハンドラに渡されます。

LEFT
MIDDLE
RIGHT
WHEEL_UP
WHEEL_DOWN
class keys

キーを示す組込みの列挙型オブジェクトで、 on_key_* ハンドラに渡されます。

BACKSPACE
TAB
CLEAR
RETURN
PAUSE
ESCAPE
SPACE
EXCLAIM
QUOTEDBL
HASH
DOLLAR
AMPERSAND
QUOTE
LEFTPAREN
RIGHTPAREN
ASTERISK
PLUS
COMMA
MINUS
PERIOD
SLASH
K_0
K_1
K_2
K_3
K_4
K_5
K_6
K_7
K_8
K_9
COLON
SEMICOLON
LESS
EQUALS
GREATER
QUESTION
AT
LEFTBRACKET
BACKSLASH
RIGHTBRACKET
CARET
UNDERSCORE
BACKQUOTE
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
DELETE
KP0
KP1
KP2
KP3
KP4
KP5
KP6
KP7
KP8
KP9
KP_PERIOD
KP_DIVIDE
KP_MULTIPLY
KP_MINUS
KP_PLUS
KP_ENTER
KP_EQUALS
UP
DOWN
RIGHT
LEFT
INSERT
HOME
END
PAGEUP
PAGEDOWN
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
NUMLOCK
CAPSLOCK
SCROLLOCK
RSHIFT
LSHIFT
RCTRL
LCTRL
RALT
LALT
RMETA
LMETA
LSUPER
RSUPER
MODE
HELP
PRINT
SYSREQ
BREAK
MENU
POWER
EURO
LAST

そのほかにも修飾キーを表す定数があります。

class keymods

on_key_up または on_key_down イベント発生のとき押されていた修飾キーを示す定数です。

LSHIFT
RSHIFT
SHIFT
LCTRL
RCTRL
CTRL
LALT
RALT
ALT
LMETA
RMETA
META
NUM
CAPS
MODE

組込みオブジェクト

Pygame Zeroには、ゲーム制作を簡単にしてくれる便利な組込みオブジェクトが用意されています。

Screen

テキストの書式整形

Screendraw.text() メソッドはテキストの表示位置や書式をできる様々なオプションを備えています。以下はそのサンプルです

screen.draw.text("Text color", (50, 30), color="orange")
screen.draw.text("Font name and size", (20, 100), fontname="boogaloo", fontsize=60)
screen.draw.text("Positioned text", topright=(840, 20))
screen.draw.text("Allow me to demonstrate wrapped text.", (90, 210), width=180, lineheight=1.5)
screen.draw.text("Outlined text", (400, 70), owidth=1.5, ocolor=(255,255,0), color=(0,0,0))
screen.draw.text("Drop shadow", (640, 110), shadow=(2,2), scolor="#202020")
screen.draw.text("Color gradient", (540, 170), color="red", gcolor="purple")
screen.draw.text("Transparency", (700, 240), alpha=0.1)
screen.draw.text("Vertical text", midleft=(40, 440), angle=90)
screen.draw.text("All together now:\nCombining the above options",
    midbottom=(427,460), width=360, fontname="boogaloo", fontsize=48,
    color="#AAFF00", gcolor="#66AA00", owidth=1.5, ocolor="black", alpha=0.8)

基本的な使い方は簡単です。 screen.draw.text に表示したいテキストとその位置を指定するだけです。位置は2番目の引数として(テキストの左上隅の)座標で指定します。そのほか後述するキーワードを使った指定も可能です

screen.draw.text("hello world", (20, 100))

screen.draw.text には次のように様々なオプションのキーワード引数も指定できます。

フォント名とサイズ

フォントは fonts という名前のディレクトリからロードされます。これは images や sounds と同様の仕組みです。なおサポートしているフォントの形式は .ttf です。たとえばこのように指定します

screen.draw.text("hello world", (100, 100), fontname="Viga", fontsize=32)
  • fontname: 表示に使うフォント・ファイルの名前です。特に指定の無いときは、デフォルトでシステム・フォントを使用します。
  • fontsize: フォントのサイズをピクセル数で指定します。デフォルトは24ピクセルです。
  • antialias: アンチエイリアスを使用するかどうかの指定です。デフォルトは True で使用します。
色と背景色
screen.draw.text("hello world", (100, 100), color=(200, 200, 200), background="gray")

キーワード引数は次の通りです。

  • color: 文字の色を指定します。デフォルトは white です。
  • background: 文字の背景色を_指定します。デフォルトは None で「無し」です。

color (そのほか backgroundocolorscolor, gcolor なども)色は (赤, 緑, 青) のシーケンス、たとえば (255,127,0) のように指定します。そのほか "orange" のような pygame.Color オブジェクトのカラー名、 "#FF7F00" のような HTML の16進数文字列、または "0xFF7F00" のような16進数文字列を使っての指定も可能です。

background には None を指定することもでき、その場合背景は透明になります。pygame.font.Font.render とは違い、 screen.draw.text を呼び出すたびに背景色を指定するのは効率が良くありません。実際に必要な場合だけ背景色を指定するようにしてください。

色の透明度をアルファ値で指定する方法はサポートされていません(ただしアウトラインまたはドロップシャドウを持つ見えないテキストは例外です。これは後で解説します)。透明度についてはキーワード引数 alpha の項を読んでください。

位置指定
screen.draw.text("hello world", centery=50, right=300)
screen.draw.text("hello world", midtop=(400, 0))

使用できるキーワード引数は次の通りです。

top left bottom right
topleft bottomleft topright bottomright
midtop midleft midbottom midright
center centerx centery

位置指定のキーワード引数の使い方は pygame.Rect の位置指定プロパティと同様です。ボックスの横の位置と縦の位置に対応する2つの引数を指定するか、または両方の指定を単一の引数で渡します。

位置が過剰に指定されている(たとえば、 leftright の両方が指定されている)場合、余分な指定は(任意に、しかし決定論的に)無効にされます。「制約付きテキスト」については、後述の screen.draw.textbox を参照してください。

ワードラップ
screen.draw.text("splitting\nlines", (100, 100))
screen.draw.text("splitting lines", (100, 100), width=60)

キーワード引数は以下の通りです。

  • width: テキスト表示に使う最大幅をピクセル数で指定します。デフォルトは None です。
  • widthem: テキスト表示に使う最大幅をフォントの em で指定します。デフォルト None です。
  • lineheight: 行の間隔をフォントのデフォルトの行間を単位に指定します。 デフォルトは 1.0 です。

screen.draw.text は改行文字 \n で常に改行します。 widthwidthem が指定されている場合は、どの行もその幅を越えないように折り返そうとします。しかしすべての行のテキストが必ず指定された幅に収まるとは限りません。折り返しは空白文字のところでしかできないためです。ですから、空白を含まない長い単語があると、折り返せなくなってしまいます。アウトラインとドロップシャドウも考慮されないため、指定の幅を越えて表示される可能性があります。

スペースでの折り返しを抑制したいときはノーブレークスペース(\u00A0)を使っ てください。

テキスト・アライメント
screen.draw.text("hello\nworld", bottomright=(500, 400), align="left")

キーワード引数は以下の通りです。

  • align: 行がお互いに配置される水平位置を指定します。デフォルトは None です。

align は複数の行を表示する際の、お互いの行が配置される水平位置を決定します。指定できる値は文字列 "left", "center", または "right" です。数値を使って 0.0 (左寄せ) から 1.0 までの値を指定することもできます。さらに None も指定できます。

alignNone を指定した場合の水平位置は、他の引数の内容に左右されます。位置指定の引数(topleft, centerx など)、 anchor の設定に影響を受け、最終的なデフォルトは "left" となります。

通常はデフォルトの配置を使い、どうしてもバランスが悪く見えてしまうときにだけ align を使うことをお勧めします。

アウトライン(文字の縁取り)
screen.draw.text("hello world", (100, 100), owidth=1, ocolor="blue")

キーワード引数は以下の通りです。

  • owidth: 文字のアウトラインの太さを指定します。デフォルトは None です。
  • ocolor: アウトラインの色を指定します。デフォルトは "black" です。

owidth を指定すると文字にアウトラインが付きます。アウトライン処理はあまり細かに行われないため、文字サイズを大きくするとおそらく見た目が悪くなります。 owidth の値は 1.0 としたときにちょうどよく表示されるようになっています。フォントサイズを 24 で割った値がその単位です。

特別なケースとして、アウトラインを使用する際、 color に透明な値(たとえば (0,0,0,0)) を指定するとテキストが見えなくなり、文字の中が空になったアウトラインだけが表示されます(この機能は後述の gcolor との互換性はありません)。

ocolor に指定できる色の値は color と同じです。

ドロップシャドウ
screen.draw.text("hello world", (100, 100), shadow=(1.0,1.0), scolor="blue")

キーワード引数は以下の通りです。

  • shadow: ドロップシャドウを表示するオフセット座標 (x,y) を指定します。デフォルトは None です。
  • scolor: ドロップシャドウの色を指定します。デフォルトは "black" です。

shadow を指定すると文字にドロップシャドウが表示されます。引数にはオフセット座標の x と y の値をタプルで指定します。数値は正の値、負の値、0 の何れも使用可能です。たとえば shadow=(1.0,1.0) の場合、テキストの右下に影が付きますが、shadow=(0,-1.2) にすると、影はテキストの上に表示されます。

shadow の値は 1.0 としたときにちょうどよく表示されるようになっています。フォントサイズを 18 で割った値がその単位です。

特別なケースとして、ドロップシャドウを使用する際、 color に透明な値(たとえば (0,0,0,0)) を指定するとテキストが見えなくなり、影だけが表示されます(この機能は後述の gcolor との互換性はありません)。

scolor に指定できる色の値は color と同じです。

グラデーション
screen.draw.text("hello world", (100, 100), color="black", gcolor="green")

キーワード引数は以下の通りです。

  • gcolor: グラデーションを停止するときの色を指定します。デフォルトは None です。

テキストに垂直方向のグラデーションを付ける場合は gcolor を指定します。テキストの色は上端が color で下端が gcolor になります。グラデーションの停止位置と方向はハードコードされているため、変更することはできません。

アルファ値を使った透明度指定
screen.draw.text("hello world", (100, 100), alpha=0.5)

キーワード引数は以下の通りです。

  • alpha: 0 から 1 までのアルファ値を使って透明度を指定します。デフォルトは 1.0 です。

透明表示の際、キャッシュを使い値の再利用を最大化するため、alpha の値は丸められます。

アンカーを使った配置
screen.draw.text("hello world", (100, 100), anchor=(0.3,0.7))

キーワード引数は以下の通りです。

  • anchor: タプルの2つの要素でそれぞれ垂直と水平のアンカーを指定します。デフォルトは (0.0, 0.0) です。

位置指定のキーワード引数を指定しない場合、anchor を使ってテキストをどのように配置するか指定できます。anchor の2つの値にはそれぞれ 0.0 から 1.0 までの任意の値を指定できます。 anchor の値 (0,0) はデフォルトで、テキストの左上が座標の位置になります。値を (1,1) としたときは、テキストの右下が座標の位置になります。

回転
screen.draw.text("hello world", (100, 100), angle=10)

キーワード引数は以下の通りです。

  • angle: 時計回りの回転角度を指定します。デフォルトは 0 です。

回転させたときの Surface の位置指定は厄介です。回転したテキストを描画するとき、実際に指定した位置はアンカーとして固定され、テキストはその周りを回転することになります。たとえば、テキストの左上の座標を (100, 100) にして角度を 90 にした場合、実際には Surface の左下を (100, 100) にして描画されることになります。

これが分かりにくいようだったら、アンカーがテキストの中心に指定してみてください。アンカーをテキストの中心にすれば、いくら回転させても常にテキストの中心で変わることはありません。

回転させた Surface の描画の際は、キャッシュを使い値の再利用を最大化するため、angle の値は 3 の倍数に丸められます。

テキストのサイズ制限
screen.draw.textbox("hello world", (100, 100, 200, 50))

screen.draw.textbox には2つの引数を指定します。ひとつは描画するテキストで、もうひとつは pygame.Rect オジェクト、あるいは Rect と同じように x 座標、y 座標、幅、高さの値を要素に持つタプルで指定します。フォントのサイズは範囲内に収まる最大のものが自動的に選択されます。 fontsize と位置指定を除き、 screen.draw.textbox には screen.draw.text と同じキーワード引数が指定できます。

screen オブジェクトはゲーム・スクリーンを表します。

これは Pygame surface の薄いラッパーで、スクリーンへの描画や画像データの表示を容易にしてくれます。

class Screen
surface

スクリーン・バッファを表す素の Pygame surface そのものです。高度なグラフィック操作をしたいときに使用します。

bounds()

バージョン 1.3 で追加.

スクリーンの領域を表す ZReact を返します。

clear()

スクリーンを黒にリセットします。

fill((red, green, blue)[, gcolor=(r, g, b)])

スクリーンを単色で塗り潰します。

バージョン 1.3 で追加: gcolor を指定するとグラデーションを使って塗り潰します。その場合、スクリーンの上端が color 、下端が gcolor となります。

blit(image, (left, top))

指定の位置に画像を描画します。

blit() の引数 image には Surface オブジェクトと文字列、どちらでも指定できます。もし image が文字列だった場合は、 images/ ディレクトリからその名前の画像を探してロードします。

draw.line(start, end, (r, g, b))

start の座標から end の座標まで直線を描画します。

draw.circle(pos, radius, (r, g, b))

円の輪郭を描画します。

draw.filled_circle(pos, radius, (r, g, b))

塗り潰しで円を描画します。

draw.rect(rect, (r, g, b))

四角形の輪郭を描画します。

四角形の領域指定には Rect を使います。

draw.filled_rect(rect, (r, g, b))

塗り潰しで四角形を描画します。

draw.text(text, [pos, ]**kwargs)

テキストを描画します。

表示位置やフォーマットを細かに指定できる API もあります(詳細は テキストの書式整形 を参照)。

draw.textbox(text, rect, **kwargs)

引数に指定された Rect 領域の大きさでテキストを描画します。

表示位置やフォーマットを細かに指定できる API もあります(詳細は テキストの書式整形 を参照)。

Rect

Pygame Rect クラスは組込みのオブジェクトとして利用可能です。このオブジェクトは、スクリーン上のクリックされた領域に四角形を描画するときなど、様々な使い方ができます。

たとえば四角形の描画はこのようにします

RED = 200, 0, 0
BOX = Rect((20, 20), (100, 100))

def draw():
    screen.draw.rect(BOX, RED)

リソースのロード

imagessounds オブジェクトはそれぞれ imagessounds サブディレクトリに保存されているファイルから、画像とサウンドをロードする際に使われます。Pygame Zero はこれらのリソースを必要に応じてロードし、何度もロードし直さないようにキャッシュします。

通常、画像のファイル名には小文字、数字とアンダースコアだけを使います。また先頭は数字ではなく、文字で始まるようにしてください。

次のようなファイル名ならリソース・ローダは問題なく動作します

alien.png
alien_hurt.png
alien_run_7.png

しかし次のような名前だと動きません

3.png
3degrees.png
my-cat.png
sam's dog.png

リソース・ローダーは画像やサウンドをキャッシュします。(たとえばメモリ容量の制限などの理由で)キャッシュをクリアしたいときは、関数 unload()unload_all() を使用します。

cow = Actor('cow')
loader.images.unload('cow')  # キャッシュから cow.png をクリア
loader.images.unload_all()  # すべての画像キャッシュをクリア
Images

Pygame Zeroでロード可能な画像の形式は .png.gif.jpg です。高品質で透過表示をサポートしている PNG の利用をお勧めします。

images ディレクトリは予め作成しておく必要があります。たとえばプロジェクトに次のようなファイルがあるとします

space_game.py
images/alien.png

この状態で space_game.py に次のようなコードを書くとスクリーンに alien スプライト描画されます

def draw():
    screen.clear()
    screen.blit('alien', (10, 10))

blit() に渡される名前は images ディレクトリに置かれている画像のファイル名です。ただし拡張子は付けません。

Actors APIを使う方法もあります

alien = Actor('alien')

def draw():
    alien.draw()

上記何れの場合も付けられるファイル名には制限があります。使用できるのは小文字のアルファベット、数字とアンダースコアだけです。これはゲームを動かすオペレーティング・システムによって大文字、小文字の扱いが異なる場合があり、問題が発生することを防ぐためです。

Image Surfaces

images ディレクトリに置かれた画像を images オブジェクトを使ってロードすることもできます。この方法を使うと、画像データそのものを操作したり、サイズを問い合わせることが可能になります

forest = []
for i in range(5):
    forest.append(
        Actor('tree', topleft=(images.tree.get_width() * i, 0))
    )

ロードした画像は Pygame の Surface オブジェクトになります。典型的な使い方は screen.blit(...) でスクリーンに描画することです。このオブジェクトは画像のサイズをピクセル単位で問い合わせできるメソッドを備えています。

class Surface
get_width()

画像の幅をピクセル単位で返します。

get_height()

画像の高さをピクセル単位で返します。

get_size()

ピクセル単位の画像のサイズを(幅, 高さ)のタプルにして返します。

get_rect()

画像が原点に位置していた場合、画像の境界のサイズが設定された Rect を返します。

これは次のコードと同等の内容です

Rect((0, 0), image.get_size())
Sounds

Pygame Zeroでロード可能なサウンドの形式は .wav.ogg です。WAV は サイズの小さな効果音に最適で、圧縮フォーマットの OGG は音楽に適しています。ゲームに使用できる .ogg や .wav ファイルをインターネット上で無償提供しているサイトもあります。

sounds ディレクトリは予め作成しておく必要があります。たとえばプロジェクトに次のようなファイルがあるとします

drum_kit.py
sounds/drum.wav

drum_kit.py に次の内容を書くと、マウスがクリックときドラムの音を鳴らすようになります

def on_mouse_down():
    sounds.drum.play()

ロードしたサウンドは Pygame の Sound オブジェクトになります。このオブジェクトは様々なメソッドを備えており、サウンドの再生、停止やサウンドの再生時間の問い合せをサポートしています。

class Sound
play()

サウンドを再生します。

play(loops)

サウンドを指定した回数、繰り返し再生します。

パラメータ:loops -- 繰り返し再生する回数。この回数を -1 を指定すると、無限に(もしくは Sound.stop() で停止するまで)繰り返し再生し続けます。
stop()

サウンドの再生を停止します。

get_length()

サウンドの再生時間を秒単位で取得します。

長い音楽を再生するために sounds オブジェクトを使用することは避けてください。サウンドのシステムは再生前に音楽データをすべてメモリ上にロードするので大量のメモリが必要になることがあり、ロード完了まで遅延が発生する可能性があります。

Music

バージョン 1.1 で追加.

警告

Music API はまだ実験段階のものなので、クロスプラットフォームの移植性問題が発生する可能性があります。

たとえば

  • Linux ディストリビューションによっては MP3 をサポートしていないものがあります。
  • OGG Vorbis のファイルによっては CPU パワーが 100 パーセント消費されてしまい Pygame がハングしてしまうことがあります。

後者については、サウンド・データを(できれば別のエンコーダを使って)エンコードし直すことで、解決できる可能性があります。

組込みオブジェクト musicmusic/ ディレクトリに置かれた音楽を再生するための手段を提供します。(images/sounds/ が存在する場合も並行で利用可能です)

Music を使った場合、音楽データは少しずつロードされるため、 sounds で長い音楽を再生したときに発生する問題を回避できます。

もうひとつ、サウンドシステムと異なる点は、一度にひとつのトラックしか再生できないことです。もし何か再生中に別のトラックを再生すると、前に再生していたトラックは自動的に停止します。

music.play(name)

指定されたファイルの音楽トラックを再生します。トラックは無限にループ再生されます。

もし前に再生中のトラックがあればそれを置き換え、 queue() で待機中のトラックをキャンセルします。

トラック名にファイルの拡張子を付ける必要はありません。たとえばファイル handel.mp3 をループ再生したいときは次のように書きます

music.play('handel')
music.play_once(name)

play() と同様に音楽を再生しますが、一度再生したら停止します。

music.queue(name)

play_once() と同様一度だけの再生を意図するものですが、既に再生中のトラックがあってもそれを停止せず、次に再生する曲をキューに入れ、前の曲が完了してから再生します(キュー内にほかのトラックがある場合は、それらがすべて完了してから再生されます)。

music.stop()

音楽の再生を停止します。

music.pause()

音楽の再生を一時停止します。 unpause() の呼び出しで一時停止は解除できます。

music.unpause()

音楽再生の一時停止を解除します。

music.is_playing()

音楽が再生中でかつ一時停止されていなければ True を返し、それ以外だったら False を返します。

music.fadeout(duration)

フェードアウトして、最終的に現在の音楽再生を停止します。

パラメータ:duration -- 音が消えるまでの時間を秒で指定します。たとえば 0.5 秒でフェードアウトしたいなら music.fadeout(0.5) とします。
music.set_volume(volume)

ミュージック・システムのボリュームを設定します。

0(無音)から1(最大)までの値を指定できます。

music.get_volume()

現在のミュージック・システムのボリュームを取得します。

music.play_once() を使って音楽トラックの再生を開始した場合、 on_music_end() hook を使って再生終了時に何かを実行させることができます。これはたとえば、ランダムに別のトラックを選びたいときなどに有効です。

Clock

ゲームを作成する場合、時間を遅らせてゲームイベントを発生させたいことがしばしばあります。たとえば 60 秒後に大きなボスのエイリアンを登場させたり、20 秒ごとにパワーアップのアイテムを表示したり、などです。

もっと微妙に、わずかな時間だけアクションを遅らせたいこともあります。たとえば、パワー補充に1秒かかるレーザー兵器とか。

clock オブジェクトを使うと、時間を置いて実行する関数のスケジュールができます。

では、時間を置いて実行したい関数 fire_laser を定義することから始めましょう

def fire_laser():
    lasers.append(player.pos)

それから発射ボタンが押されると、正確に1秒後にこの関数を呼び出すよう clock に依頼します

def on_mouse_down():
    clock.schedule(fire_laser, 1.0)

ここで fire_laser の後にはカッコを書いていません。関数を実行しているのではなく、関数そのものを渡していることに注意してください。この関数は clock が実行してくれるのです。

(1.0 のように小数点付きの秒数で時間を指定するのは良い習慣です。後でコードを読み返したとき、この値が時間であり、物の数ではないことがわかるからです。)

clock は以下の便利なメソッドを提供しています。

class Clock
schedule(callback, delay)

指定の時間経過後に callback を呼び出すようスケジュールします。

このメソッドの呼び出しを繰り返すと、コールバックも繰り返しスケジュールされます。

パラメータ:
  • callback -- 引数を取らない呼び出し可能オブジェクト。
  • delay -- 関数を呼び出すまでの遅延時間を秒で指定します。
schedule_unique(callback, delay)

指定の時間経過後に callback を一度だけ呼び出すようスケジュールします。

もし同じ callback が既にスケジュールされていたら、一旦それをキャンセルして、スケジュールの再登録が行われます。

これは複数回スケジュールされた場合にも当てはまります。 schedule_unique を呼び出した後、で複数回呼び出してもひとつしかスケジュールされません。

パラメータ:
  • callback -- 引数を取らない呼び出し可能オブジェクト。
  • delay -- 関数を呼び出すまでの遅延時間を秒で指定します。
schedule_interval(callback, interval)

callback を繰り返し呼び出すようにスケジュールします。

パラメータ:
  • callback -- 引数を取らない呼び出し可能オブジェクト。
  • interval -- callback を呼び出す間隔を秒で指定します。
unschedule(callback)

既にスケジュールされているコールバックをキャンセルします。 schedule() でスケジュールされてまだ呼び出されていなもの、または schedule_interval() で繰り返しがスケジュールされているものをキャンセルします。

Pygame Zero の clock は指定されたコールバックへの弱参照しか保持しないことに注意してください。オブジェクトとメソッドが他の場所で参照されていない場合、スケジュールされたイベントは発生しません。これは、clock がオブジェクトを生き続けさせ、何らかの理由で他のオブジェクトが消えてしまった後にイベントが発生し続けるのを防ぐのに役立ちます。

弱参照の欠点は、無名関数や他から参照されていないそのスケジュールだけのオブジェクトはスケジュールできないことです。オブジェクトへの参照は保持しておく必要があります。

Actors

ゲーム内で動く画像が多くなってくると、画像とそのスクリーン上の位置をまとめて保持してくれるものがあると便利です。それがスクリーン上の個々の動く画像を管理する Actor (アクター)です。Actor は(前述の images ディレクトリ内にある)画像ファイル名を指定するだけで生成できます。前にも使ったエイリアンをアクターを使って表示してみます

alien = Actor('alien', (50, 50))

def draw():
    screen.clear()
    alien.draw()

位置の属性を関数 update の中で変更することで、Actor を動かすことができます

def update():
    if keyboard.left:
        alien.x -= 1
    elif keyboard.right:
        alien.x += 1

また、Actor の属性 image を別の新たな画像名に変更することで、表示中の画像を変更することもできます

alien.image = 'alien_hurt'
Actor は Rect と同じ属性とメソッドを備えています。そのメソッドのひとつ
.colliderect() を使うと二つの Actor が衝突しているかどうかを調べること ができます。
Actor の位置指定

位置の属性に新たな値を代入すると、Actor は移動します。たとえば

alien.right = WIDTH

この例ではエイリアンの右端の位置を WIDTH にしています。

同様に最初の表示位置を Actor のコンストラクタで指定することもできます。使用できるキーワード引数は pos, topleft, topright, bottomleft, bottomright, midtop, midleft, midright, midbottom, center の何れかです。

_images/anchor_points.png

次のようにオブジェクト生成の際や、x と y の座標を指定する方法でも使えます

WIDTH = 200
HEIGHT = 200

alien = Actor('alien', center=(100,100))

def draw():
    screen.clear()
    alien.draw()
_images/alien_center.png

center=(100, 100) の部分を midbottom=(100, 200) に変えると次のようになります。

_images/alien_midbottom.png

初期位置を指定しなかった場合, Actor は左上隅(topleft=(0, 0) を指定した場合と同じ)に表示されます.

アンカーポイント

Actor には「アンカー(anchor)ポイント」というものがあります。これはシーン内で Actor を配置するときに役立ちます。アンカーポイントのデフォルトは center (中心)です。これにより .pos も Actor の中心となります(xy の座標も同様です). アンカーポイントはスプライトの別の部分(たいてい足)に設定するのが一般的です。そうすることで Actor を何かの上に立たせる設定が可能になります

alien = Actor('alien', anchor=('center', 'bottom'))
spaceship = Actor('spaceship', anchor=(10, 50))

anchor(x 座標のアンカー, y 座標のアンカー) のタプルで指定します。それぞれの値は浮動小数点数または文字列で、文字列の場合 left, center, middle, right, top, bottom の何れかを適宜指定します。

Rotation

バージョン 1.2 で追加.

.angle はスプライトの回転をコントロールする Actor の属性です。反時計回りで角度を指定します。

回転の中心は Actor の アンカーポイント になります。

回転すると Actor の widthheight が変化することに注意してください。

次の例は宇宙空間で小惑星がゆっくりと反時計回りに回転するものです

asteroid = Actor('asteroid', center=(300, 300))

def update():
    asteroid.angle += 1

時計回りに回転させたいときは、 update() を次のように変更します

def update():
    asteroid.angle -= 1

もうひとつ、次の例は ship (宇宙船) Actor の正面が常にマウスポインタの方向へ向くようにするものです。マウスポインタが右にある場合 angle_to() は 0 を返すため、宇宙船のスプライトも右を向きます

ship = Actor('ship')

def on_mouse_move(pos):
    ship.angle = ship.angle_to(pos)
angle_to() を使ってスプライトを回転させる方法

角度は1回転すると 0 に戻ります。ですから 360 度と 720 度はどちらも 0 度と同じになります。また -180 度は 180 度と同じです。

距離と角度

バージョン 1.2 で追加.

Actor には他の Actor や (x, y) 形式の座標との距離や角度を計算してくれる便利なメソッドがあります。

Actor.distance_to(target)

この Actor の位置から target までの距離をピクセル数で返します。

Actor.angle_to(target)

この Actor の位置から target までの角度を度で返します。

このメソッドは -180 度から 180 度までの値を返します。右が 0 度で、反時計回りに数値が増加します。

したがって次のようになります。

  • 左は 180 度
  • 上は 90 度
  • 下は -90 度
透明度

バージョン 1.3 で追加.

場合によって、Actor オブジェクトを部分的に透明にできると便利なことがあります。この手法はフェードイン、フェードアウト表示のほかに、それが「使えない」状態にあることを表現したいときなどに使われます。

透明度は Actor の .opacity 属性でコントロールします。

  • Actor が完全に不透明の場合、その opacity1.0 であり、その背後はまったく見えません。
  • Actor が完全に透明の場合、その opacity0.0 であり、見えない状態となります。

Actor を幽霊のように半透明にしたいときは、次のようにします

ghost = Actor('ghost')
ghost.opacity = 0.5

次の図は透明度の尺度を示しています。なお灰色のチェッカーボードは透明感表現するのに使用しています。

Pygame Zero での透明度の尺度

ちなみに

透明なオブジェクトが重なる場合、その描画順は重要です。窓ごしに見える幽霊と幽霊の背後に透けて見える窓はちょっと違いますよね。

キーボード

keyboard オブジェクトは既にこれまでのコードの中に登場していることに気付いていますよね。どのキーが押されているのか知りたいときは、組込みオブジェクト keyboard の属性を調べます。たとえば左矢印キーが押されているときは keyboard.leftTrue になります。それ以外のキーの場合だったら False になります。

各キーに対応した属性がありますが、その一部を例として紹介します

keyboard.a  # 'A' キー
keyboard.left  # 左矢印キー
keyboard.rshift  # 右シフトキー
keyboard.kp0  # テンキーの '0'
keyboard.k_0  # メインの '0' キー

すべてのキーの定数は マウスのボタンとキー に記されている通りですが、属性は小文字であることに注意してください。キーボードの属性は定数ではありません。

バージョン 1.1 で非推奨: 大文字で始まる属性名(keyboard.LEFTkeyboard.K_a など)は廃止になりました。小文字の属性名を使用してください。

バージョン 1.1 で追加: キーの状態はキーボード定数を使っても調べられるようになりました

keyboard[keys.A]  # 'A' キーが押されていたら True
keyboard[keys.SPACE]  # スペースキーが押されていたら True

Animation

Pygame のたいていのものは、組込みの関数 animate() を使ってアニメーションにできます。たとえば Actor を現在の位置から (100, 100) へ動かす場合、次のように書きます。

animate(alien, pos=(100, 100))
animate(object, tween='linear', duration=1, on_finished=None, **targets)

オブジェクトの属性を、現在の値からキーワード引数 target に指定された値までのアニメーションにします。

パラメータ:
  • tween -- 使用するアニメーション トゥイーン のタイプ。
  • duration -- アニメーションの開始から終了までの時間を秒で指定。
  • on_finished -- アニメーション終了時に呼び出す関数。
  • targets -- アニメーション化する属性のターゲット値。

キーワード引数 tween には以下の何れかの値を指定できます。

'linear' アニメーションの最初から最後まで一定のスピードで動かします _images/linear.png
'accelerate' ゆっくりしたスピードで始め、だんだん早くします _images/accelerate.png
'decelerate' 早いスピードで始め、だんだん遅くします _images/decelerate.png
'accel_decel' アニメーション時間のちょうど中間が一番早くなるようにします _images/accel_decel.png
'in_elastic' アニメーションの最後に若干の揺らぎを入れます _images/in_elastic.png
'out_elastic' アニメーションの最初に若干の揺らぎを入れます _images/out_elastic.png
'in_out_elastic' アニメーションの最初と最後に揺らぎを入れます _images/in_out_elastic.png
'bounce_end' スピードをだんだん早くして最後に弾みを入れます _images/bounce_end.png
'bounce_start' アニメーションの最初に弾みを入れます _images/bounce_start.png
'bounce_start_end' アニメーションの最初と最後に弾みを入れます _images/bounce_start_end.png

関数 animate()Animation のインスタンスを返します。

class Animation
stop(complete=False)

アニメーションを停止します。オプションで停止する際の属性値を指定できます。

パラメータ:complete -- アニメーションが停止する際の属性値を指定します。
running

このメソッドが True を返したら、アニメーションは動作中です。アニメーションが完了または stop() メソッドにより停止したときは False を返します。

on_finished

アニメーションが完了したときに呼び出す関数をこの属性にセットできます。 関数 animate() でキーワード引数 on_finished を指定した場合も、この属性にその内容が引き継がれます。ただし stop() で停止したときは、関数は呼び出されません。またこの関数に引数は指定できません。

トーン・ジェネレータ

バージョン 1.2 で追加.

Pygame Zero 組込みのシンセサイザーを使って音を再生することができます。

tone.play(pitch, duration)

指定された高さと、長さで単音を再生します。

長さは秒数で指定します。

pitch は周波数の単位ヘルツを数値で指定します。

上記のほか pitch を音名で指定する方法もあります。この場合、音名とオクターブの文字列を使います。以下はその例です。

  • 'E4' は第 4 オクターブ E です。
  • 'A#5' は第 5 オクターブの A シャープです。
  • 'Bb3' は第 3 オクターブの B フラットです。

音の生成、特に長い音の生成には時間がかかります(最大で数ミリ秒)。あらかじめ必要な音を生成しておくと、ゲームの実行を遅くせずに済みます。

tone.create(pitch, duration)

Sound オブジェクトを生成して返します。

再生用のメソッド play() の引数などは前に述べた通りです。

Pygame Zero プログラムの中で次のような使い方をします

beep = tone.create('A3', 0.5)

def on_mouse_down():
    beep.play()

データ・ストレージ

storage オブジェクトは Python の辞書に似ていますが、その内容はゲーム・セッションを通じて保存されます。ストレージにセットした値は JSON 形式で保存されます。したがって保存可能なデータの型は list/tuple, dict, str, float/int, bool, None に限られます。

ゲームの storage の初期状態は空です。したがってプログラムは、ストレージから読み込んだときに、値が何もない場合を考慮して作成する必要があります。

ヒントは setdefault() を使うことです。これはキーに対応する値が無い場合はデフォルトの値を挿入するようになっています。値があるときは何もしません。

たとえば次のようにして使います

storage.setdefault('highscore', 0)

上記の行が実行されると、 storage['highscore'] に対応する値が存在しない場合は値 0 がセットされ、存在するときは何もしません。

他のコードが storage を参照する前、 ゲームの冒頭で setdefault を記述しておくようにしましょう

storage.setdefault('level', 1)
storage.setdefault('player_name', 'Anonymous')
storage.setdefault('inventory', [])

そして、ゲームが始まったら値を次のように更新します

if player.colliderect(mushroom):
    score += 5
    if score > storage['highscore']:
        storage['highscore'] = score

ストレージにセットした値はいつでも読み出せます

def draw():
    ...
    screen.draw.text('Highscore: ' + storage['highscore'], ...)

そしてもちろんゲームを終了しても、次に起動したときのために値は保存されます。

storage のメソッドの中でもよく使われるものは以下の通りです。

class Storage(dict)
storage[key] = value

値 value をストレージにセットします。

storage[key]

ストレージから値を取得します。指定した key に対応する値がストレージに無いときは KeyError が発生します。

setdefault(key, default)

指定した key に対応する値がまだストレージに存在しない場合に限り、 default で指定した値をセットします。

get(key, default=None)

ストレージから値を取得します。指定した key に対応する値が無い場合、default の指定があればその値を、無ければ None を返します。

clear()

ストレージの値すべてを削除します。何かおかしくなってやり直したいときは、これを使ってください。

save()

データを直ちにディスクに保存します。通常このメソッドを呼び出す必要はありませんが、チェックポイントで load() を使って再読み込みを実行したいときなどは、その前にこれを呼び出す必要があります。

load()

保存ファイルからデータをストレージに再読み込みします。これを実行すると、既存のストレージのデータはすべて置き換えられてしまうので注意してください。

path

ストレージ・データの書き込み先ファイルのパスです。

ご用心

ゲーム・プログラムを変更すると、 storage に保存されている値そのままでは正しく動かなくなることがあります。まず間違いがないか確認し、必要があれば .clear() で一旦すべてのデータをクリア、またはストレージのデータファイルを削除してください。

ちなみに

ストレージが空のときも、ゲームがちゃんと動くことを確認しておきましょう。

ユーザーガイド

Pygame Zero のインストール

Mu エディタに同梱のものを使う

Mu エディタ はプログラミング初心者向けのエディタで、あらかじめ Pygame Zero が同梱されています。

モードの選択 で Pygame Zero 選ぶと Pygame Zero が使えるようになります。プログラム入力後、 プレイのボタン を押すと Pygame Zero で実行されます。

ご用心

Mu エディタに同梱されている Pygame Zero のバージョンは最新ではない場合もあります。Mu に次のコードを入力し、実行することでバージョンを確認できます

import pgzero
print(pgzero.__version__)

スタンドアローン版のインストール

まず最初に Python 3 をインストールする必要があります。 LinuxRaspberry Pi を使っているなら、あらかじめインストールされているはずです。そのほかのシステムを使っている場合は python.org <https://www.python.org/> からダウンロードしてください。

Windows

Pygame Zero のインストールには pip を使います。 コマンドプロンプト で次のように入力してください。

pip install pgzero
Mac

ターミナルで次のように入力してください

pip install pgzero

Mac 版 Python 3.4 をサポートした Pygame の wheel は提供されていないので注意してください。このためバージョン 3.6 以上(または 2.7)の Python を使う必要があります。利用可能な wheel のリストについては pyPI_ を参照してください。

Linux

ターミナル・ウィンドウで次のように入力してください

sudo pip install pgzero

Linux のディストリビューションやバージョンによっては pip3 となっている場合があります。もし sudo: pip: command not found のようなエラーが表示されたら、次の内容を試してみてください

sudo pip3 install pgzero

pip 自体が入っておらず、自分でインストールしなくてはならない場合もあります。エラーが出るときは先のコマンドをやり直す前に次のコマンドを試してみてください

sudo python3 -m ensurepip

REPL のインストール

Pygame Zero の REPL はオプションの機能です。インストールするには pip のコマンドラインに pgzero[repl] を指定します

pip install pgzero[repl]

REPL がインストールされているかどうか分からないときに上記内容を実行しても大丈夫です。既にインストール済であっても、既存の環境を壊すことはありません。

REPL (Read-Evaluate-Print Loop) の使い方

REPL を使うと、Pygame Zero ゲームを実行しながら Python コマンドで対話的な操作が可能になります。コマンドをタイプすることにより、プログラム中の変数に基づく情報を得ることができます。この機能はやっかいなデバッグやゲーム・チューニング作業をするときに役立ちます。

_images/repl.png

REPL Read-Evaluate-Print Loop を略した名前です。その意味は

  1. Read (読み取り) キーボードから入力した内容を読み取ります
  2. Evaluate (評価) その内容をコードとして評価します
  3. Print (出力) 評価した結果を出力します
  4. Loop (ループ) ループバックして1に戻ります

これは Pygame Zero の オプション機能 です。もしまだインストールされていなければ、 REPL のインストール をする必要があります。実際に REPL を試してみて、もしインストールされていなければ Pygame Zero はその旨のメッセージを表示して教えてくれます。

REPL を使って Pygame Zero のプログラムを動かす

Pygame Zero プログラムをターミナルからコマンドを使って起動する際、 pgzrun--repl オプションを付けてください。たとえばゲーム・プログラムのファイル名が mygame.py だとしたら、次のように入力します。

pgzrun --repl mygame.py

REPL の使い方

REPL で入力する Python コードは、まるでそれが最初からゲーム・プログラムの中に入力されていたかのように評価されます。

たとえばゲーム・プログラムが次のように書かれていたとします

alien = Actor('alien', pos=(54, 60))

def draw():
    screen.clear()
    alien.draw()

起動したら REPL で alien と入力して alien オブジェクトの内容を見てみましょう

>>> alien
<Actor 'alien' pos=(54, 60)>

alien オブジェクトの属性に値をセットして、その動きを確認することもできます

>>> alien.x = 90

IDLE や 他の IDE での Pygame Zero 実行

バージョン 1.2 で追加.

通常 Pygame Zero は次のようなコマンドで実行します

pgzrun my_program.py

しかし IDLE や Edublocks などの IDE 上からは python だと実行できますが、 pgzrun は実行できません。

Pygame Zero は python を使い、普通の Python プログラムとして実行する方法も提供しています。そうするにはまず、プログラムの冒頭に次の行を追加します

import pgzrun

そして次の行をプログラムの最後に追加してください

pgzrun.go()

これはスクリーンに円を描く Pygame Zero プログラムです。この内容はそのまま IDLE にペーストして実行できます

import pgzrun


WIDTH = 800
HEIGHT = 600

def draw():
    screen.clear()
    screen.draw.circle((400, 300), 30, 'white')


pgzrun.go()

Pygame Zero に似たライブラリ

Pygame Zero は Python のトレンド、"zero" ライブラリ・シリーズのひとつとしてスタートしました。仲間たちがこれらの素晴しいライブラリを作っています。その内いくつかは、Pygame Zero と一緒に合わせて使うこともできます。

Network Zero

Network Zero を使うと複数のマシン、あるいは同一マシンの複数プロセスの相互検知とネットワーク通信を簡単に実現できます。

ご用心

Network Zero を Pygame Zero と一緒に使う場合、ブロック(ネットワークのメッセージ待ちで処理を止めてしまうこと)をさせてはいけません。Pygame Zero の中でこのブロックが発生すると、スクリーンのアニメーションや入力に対する応答処理なども止まってしまいます。 wait_for_s または wait_for_reply_s には常にオプションの値 0 秒を指定し、ブロックしないようにしてください。

GUI Zero

GUI Zero はウィンドウ、ボタン、スライダー、テキストボックスなどのグラフィカル・ユーザー・インターフェース (GUI) を作成するためのライブラリです。

ただし GUI Zero と Pygame Zero はそれぞれ異なるやり型でスクリーンの描画を行なっているため、両者を一緒に使うことはできません。

GPIO Zero

GPIO ZeroRaspberry Pi の General Purpose Input/Output (GPIO) ピンに接続されたデバイスを制御するためのライブラリです。

GPIO Zero 自体が独自のスレッドで動作するため、Pygame Zero と一緒に問題なく使うことができます。

ご用心

GPIO Zero のサンプルコードをコピーして試す際、 time.sleep() の呼び出しや while True: ループをそのまま使わないようにしてください。これらの処理は Pygame Zero のスクリーン・アニメーションや入力に対する応答処理などを止めてしまいます。関数を定期的に呼び出したいときは Clock を、フレーム毎に値のチェックをしたいときは update() を使うようにしてください。

Adventurelib

Adventurelib はテキストベースのゲームを書きやすくするためのライブラリです(ただし、何でも自動的にやってくれるわけではありません!)。

テキストベースのゲームを作るにはグラフィカルなゲームとはまったく異なるスキルが必要です。

Adventurelib は Pygame Zero よろもやや高度なレベルの Python プログラマを対象としています。

また Adventurelib は今のところ Pygame Zero と組み合わせて使うことはできません。

Blue Dot

Blue Dot を使うと、Android デバイスを Bluetooth リモート・コントローラにして、Raspberry Pi プロジェクトをワイヤレスで制御できます。

Blue Dot は通常、自身の独自のスレッドで動作するため、問題なく Pygame Zero と組み合わせて使うことができます。

ご用心

Pygame Zero と一緒に使う場合、 time.sleep() の呼び出し、 while True: ループや Blue Dot の wait_for_press および wait_for_release の使用は避けてください。これらの処理は Pygame Zero のスクリーン・アニメーションや入力に対する応答処理などを止めてしまいます。関数を定期的に呼び出したいときは Clock を、フレーム毎に値のチェックをしたいときは update() を使うようにしてください。

ちなみに

ここに掲載した以外のライブラリをご存じですか?

もし知っていたらイシュートラッカーに イシューを登録して 知らせてください。

Changelog

1.3 - unreleased

  • New: Actors can be made transparent by assigning to actor.opacity (based on work by Rhys Puddephatt and charlesej)
  • New: screen.fill() now takes gcolor, creating a vertical-linear gradient
  • New: a REPL has been added, which allows exploring a game's state while it is running.
  • New: Added a storage API, which preserves data across game runs (based on work by Ian Salmons and Gustavo Ferreira)

1.2 - 2018-02-24

  • New: Actors can be rotated by assigning to actor.angle
  • New: Actors now have angle_to() and distance_to() methods.
  • New: Actors are no longer subclasses of Rect, though they provide the same methods/properties. However they are now provided with floating point precision.
  • New: tone.play() function to allow playing musical notes.
  • New: pgzrun.go() to allow running Pygame Zero from an IDE (see IDLE や 他の IDE での Pygame Zero 実行).
  • New: show joypad icon by default
  • Examples: add Asteroids example game (thanks to Ian Salmons)
  • Examples: add Flappy Bird example game
  • Examples: add Tetra example game (thanks to David Bern)
  • Docs: Add a logo, fonts and colours to the documentation.
  • Docs: Documentation for the anchor point system for Actors
  • Docs: Add Scratch からの移行 documentation
  • Fix: on_mouse_move() did not correctly handle the buttons parameter.
  • Fix: Error message when resource not found incorrectly named last extension searched.
  • Fix: Drawing wrapped text would cause crashes.
  • Fix: animate() now replaces animations of the same property, rather than creating two animations which fight.
  • Updated ptext to a revision as of 2016-11-17.
  • Removed: removed undocumented British English centrex, centrey, centre attribute aliases on ZRect (because they are not Rect-compatible).

1.1 - 2015-08-03

  • Added a spell checker that will point out hook or parameter names that have been misspelled when the program starts.
  • New ZRect built-in class, API compatible with Rect, but which accepts coordinates with floating point precision.
  • Refactor of built-in keyboard object to fix attribute case consistency. This also allows querying key state by keys constants, eg. keyboard[keys.LEFT].
  • Provide much better information when sound files are in an unsupported format.
  • screen.blit() now accepts an image name string as well as a Surface object, for consistency with Actor.
  • Fixed a bug with non-focusable windows and other event bugs when running in a virtualenv on Mac OS X.
  • Actor can now be positioned by any of its border points (eg. topleft, midright) directly in the constructor.
  • Added additional example games in the examples/ directory.

1.0.2 - 2015-06-04

  • Fix: ensure compatibility with Python 3.2

1.0.1 - 2015-05-31

This is a bugfix release.

  • Fix: Actor is now positioned to the top left of the window if pos is unspecified, rather than appearing partially off-screen.

  • Fix: repeating clock events can now unschedule/reschedule themselves

    Previously a callback that tried to unschedule itself would have had no effect, because after the callback returns it was rescheduled by the clock.

    This applies also to schedule_unique.

  • Fix: runner now correctly displays tracebacks from user code

  • New: Eliminate redraws when nothing has changed

    Redraws will now happen only if:

    • The screen has not yet been drawn
    • You have defined an update() function
    • An input event has been fired
    • The clock has dispatched an event

1.0 - 2015-05-29

  • New: Added anchor parameter to Actor, offering control over where its pos attribute refers to. By default it now refers to the center.

  • New: Added Ctrl-Q/⌘-Q as a hard-coded keyboard shortcut to exit a game.

  • New: on_mouse_* and on_key_* receive IntEnum values as button and key parameters, respectively. This simplifies debugging and enables usage like:

    if button is button.LEFT:
    

1.0beta1 - 2015-05-19

Initial public (preview) release.

ステッカー by Sticker Mule

Sticker Mule は学習者向けに Pygame Zero のノートパソコン用ステッカーを提供しています。

ノートパソコン・ステッカー

うれしいことに Sticker Mule は Pygame Zero ユーザー向けにいくつものステッカーを提供してくれています。

Sticker Mule logo

ノートパソコンのステッカーは Pygame Zero の宣伝になるだけではなく、生徒たちが Pygame Zero に触れ、学習のモチベーションを上げる最適な方法です。

ステッカーのデザインはこのようになっています(実物とはサイズが違います)。

_images/sticker-mockup.svg

学習者向け

Pygame Zero はコミュニティが無償で開発しているライブラリですが、ステッカーを配布するにはコストがかかります。このため残念ながら ステッカーを直接みなさんにお届けする方法は今のところありません

ただしカンファレンスやミーティングなどでステッカーを入手できる機会があるかもしれません。

教育者およびミーティング主催者向け

ステッカーは主に教育者と教育目的の集会を通じて配布したいと考えています。

ただし現時点では何枚のステッカーを配布できるかまだ分かりません(またイギリス国外に郵送するには結構な費用がかかるかもしれません)。

興味のある方は Google Form で連絡をお願いします。

開発者向け

無償のステッカーは原則学習者たちを対象としています。ただしプルリクエストやドキュメントの翻訳がアクセプトされた人には、ステッカーを無償でお渡ししたいと思います。

プルリクエストのコメント欄にステッカーが欲しい旨を記載してください(またはカンファレンスやミーティングのときに知らせてください)。

また教育イベントや Python イベントに定期的に参加していて、ステッカーを配布しても構わないと思っている方がいたら、ご連絡ください。

Pygame Zero の改善

ロードマップ

Pygame Zero はオープンソースのプロジェクトです。他のオープンソース・プロジェクトと同様、開発ロードマップは変更になる可能性があります。

このドキュメントは将来のリリースのためのいくつかの目標を掲載していますが、 その目標が達成される保証はありません

翻訳

Pygame Zero は若いユーザーを対象にしています。しかし、若いユーザーの場合、ドキュメントが自分の言語で提供されていないと、英語版を読むだけの力がまだないことがあります。

ドキュメントの翻訳版を提供することは、新たなユーザーが Pygame Zero を使う上で大きな助けになります。これは多くの人の貢献を必要としている部分です。わたしひとりの語学力で対応しきれるものではありません!

翻訳への貢献が可能な方は 翻訳ガイド を参照してください。

ゲームコントローラのサポート

Github Issue: #70

スーパーファミコンタイプのゲームコントローラはとても安く販売されています。 Pi Hut なら数ポンド、 Amazon では2個セット、Raspberry Pi とのバンドルで販売されているものもあります。

ゲームコントローラは特定の機種定のサポートモデルだけに留めるべきではありません。今どきのほとんどのゲームコントローラが最低限備えているボタンや方向キーを最小公約数的に扱うべきだと考えています。

またこの機能は、アクセシビリティの考慮 の原則に準じ、Pygame Zero ゲームをプレイするためにゲームコントローラを 必須 としない方法で追加する必要があります。

サーフェース・ジャグリング

Github Issue: #71

Pygame のエキスパートたちは、おもしろい効果を生み出すためにオフスクリーンの Surface を多用しています。

一方 Pygame Zero はスクリーン Surface だけをサポートする方法を選択しました。これは描画などを行うリッチな API である Screen にラップして提供されています。

しかしこれによって、後から簡単に Surface を追加することができなくなっています。この件に関していえば Pygame Zero はひどく使いにくいものになっています。

Pygame Zero の Actor と Screen でうまく機能するカスタム Surface の追加方法を用意する必要があります。

ストレージ

ご用心

ストレージ API は開発が完了しており Pygame Zero 1.3 から提供される予定となっています。

Github Issue: #33

データの保存やロードに役立つ機能です。

すぐに思いつくのはゲームを保存することですが、ゲームを丸ごと保存してロードするのはかなり難しいかもしれません。より簡単な使い方は設定、カスタマイズ内容、ハイスコア、到達したレベルなどを保存することです。

もちろん Python は標準でファイルを読み書きするための API を備えていますが、こちらには先生が直ちに教えることをためらってしまう複雑さがあります。

Pygame Zero の原則

開発などにコントリビュートする前に以下の内容をよく読んでください。

Pygame Zero は初心者を対象としているため、経験の少ないプログラマに不用なハードルを設けないよう細心の注意を払う必要があります。

アクセシビリティの考慮

Pygame Zero の主な目的は、初心者プログラマが簡単に使い始められるようにすることです。もちろん API の設計も例外ではありません。

またハードウェア要件にもこの原則が当てはまります。 当初、Pygame Zero がキーボードとマウスだけしかサポートしていなかったのは、誰でも使えるようにするためです。

保守的になろう

Pygame Zero 開発の初期段階において、リチャードとわたし(ダニエル)はさまざまな機能について試行錯誤を繰り返しました。何か機能を加えて、それを試して、またそれを取り除いての繰り返しです。

実験的すぎたり、混乱をもたらしかねない機能の追加は却下しなければなりません。

この原則は OS のサポートにも当てはまります。たとえば異なる OS 間で互換性が保証されないファイル名は使えないようにしています。

とにかく動くこと

Pygame Zero は Pygame をほぼ完全にラップしたものです。しかし Pygame のすべての機能にアクセスできるようにはしていません。余計な手間をかけず本当にうまく動く機能だけをアクセス可能にし、あまりうまく動かない、または追加の手順が必要となる一部の機能は Pygame Zero から見えないようにしています。

実行時のコストを最小化する

つまるところ、Pygame Zero はゲームのフレームワークですからパフォーマンスはとても重要です。

潜在的な落とし穴を塞ぐため、フレーム毎にコストの高いチェックを行うのは、現実には受け入れ難いことです。その代わり、起動時に確認、または例外が発生した場合にのみ確認して診断の詳細を出力するようにします。

エラーを分かりやすく

Pygame Zero で例外が発生したときは、何が問題なのかを分かりやすく知らせるメッセージを出力しなければなりません。

ドキュメントを書く

他のすべてのプロジェクト同様、Pygame Zero には良いドキュメントが必要です。必要なドキュメントを含むプルリクエストはアクセプトされる確率が高くなります。

複雑な文章や技術用語はできる限り用いないようにしてください。経験の少ないプログラマにも読みやすくするためです。

破壊的な変更は極力回避

教育環境においては、使用するライブラリのバージョンを自由に選べないこともあります。最新バージョンのインストールやアップグレードの方法が分からないこともあります。

このため、一番最初に正しく機能を設計することが、他の多くのプロジェクトよりも重要です。

Pygame Zero へのコントリビュート

Pygame Zero プロジェクトは GitHub でホストされています。

バグ報告や要望を送る

Pygame Zero のバグ報告や機能の要望は Github issue tracker から送ってください。

バグ報告などを行う前に留意すべき事項は以下の通りです。

  • ほかの人も同じことをしている可能性があります。既存のイシューを検索して(open か closed)に関わらず)ほかの人が既にイシューを登録していないか確認してください。
  • 開発者はどのバージョンの Pygame Zero を使っているのか、動かしている OS (Windows, Mac, Linux 等) とそのバージョン(Windows 10, Ubuntu 16.04 等)を知る必要があります。

プルリクエストの作成方法

プルリクエストを作成することで、Pygame Zero を変更することができます。

まず最初に イシューを報告 するのが良い方法です。そうすることで変更内容が意味のあるものかどうかを議論することができます。

Github には プルリクエストの作成方法に関するヘルプ もありますが、ここにその簡易バージョンを記載します。

  1. Github にログインしていること確認してください。

  2. Pygame Zero の Github ページ へ行きます。

  3. "Fork" ボタンをクリックして自分用にリポジトリの fork を作成します。

  4. この fork を自分のコンピュータに fork します

    git clone git@github.com:yourusername/pgzero.git
    

    yourusername は自分の Github ユーザー名に置き換えることを忘れないでください。

  5. 変更を行うためのブランチを作成します。ブランチ名は変更内容を反映したものにしてください

    git checkout -b my-new-branch master
    
  6. コードの変更を行います。

  7. commit するファイルを add します

    git add pgzero
    
  8. 完結なコミット・メッセージを付けてファイルを commit します

    git commit -m "Fixed issue #42 by renaming parameters"
    

    6 から 8 までのステップを必要なだけ繰り返します。

  9. 先に fork したリポジトリに commit 内容を push します

    git push --set-upstream origin my-new-branch
    
  10. fork の Github ページへ行き、"Create pull request" ボタンをクリックします。

開発用のインストール

pip を使ってローカルに編集可能なインストールを行うことができます。チェックアウトしたソースのルートへ移動し、以下を実行してください

pip3 install --editable .

これでインストールしたバージョンにはローカルの変更内容がすべて反映されるようになります。

別の方法として、何もインストールしたくない場合は次のようにすれば実行可能です

python3 -m pgzero <pgzero のスクリプト名>

例:

python3 -m pgzero examples/basic/demo1.py

テストの実行方法

テストは次のようにすれば実行されます

python3 setup.py test

ドキュメント翻訳の手助け

Pygame Zero の API 自体は英語ですが、ドキュメントが世界各地域の言語に翻訳されていれば、もっと多くの人たちに Pygame Zero を使ってもらえます。

もしあなたが他の言語に堪能なら、ドキュメントのすべて、あるいは一部を翻訳してコントリビュートすることを、ぜひ検討してください。

ドキュメントは技術文書用のテキストベースのマークアップ言語 reStructuredText で書かれています。翻訳の際は元のドキュメントのフォーマットをできる限りそのまま生かすようにしてください。reStructuredText は慣れてしまえばさほど難しいものではありません。

ドキュメントの翻訳版を作成するには、まず Github 上にドキュメントのコピーを作成し、(少なくとも一部を)あなたがサポートしたい他の言語に書き換えてください。この方法のメリットは pgzero プロジェクト本体にプルリクエストを送る必要がなく、あなたが自分のペースで作業を進められることです。詳しい内容については Read The Docs の translation guide を参照してください。

もしやってみようかなと感じたら、以下の内容に従って始めてください。

  1. まず pgzero issue tracker で翻訳開始のイシューをオープンします。その前に、既に誰かが翻訳のイシューを登録していないか、検索して確認してください。そうすることで誰かが既に始めている翻訳作業との重複を防げます(代わりに共同して作業を進めることができます)。
  2. 自分自身の新しい Github リポジトリを pgzero-language という名前で作詞します。例: スペイン語への翻訳なら pgzero-spanish です。
  3. リポジトリを自分のコンピュータ上に clone します。
  4. Pygame Zero の doc/ ディレクトリをダウンロードして、自分のプロジェクトへ commit します。 repository ZIP file を展開するだけで OK です。余計なファイルは削除してください。
  5. ここまで準備ができたら、docs ディレクトの下にある .rst ファイルを自分の好きなエディタを使って翻訳していきます。でき上がったものは順次 commit、Github に push していきます。
  6. ステップ 1 で作成したイシューのコメントとして、リポジトリへのリンクをポストしてください。ある程度進んだら、すぐにこれを行なってください。そうすれば、興味を持った人が翻訳に協力しやすくなります。
  7. Set up the documentation to build on Read The Docs に従いドキュメントを Read The Docs にセットしてアクセスできるようになったら、再びその旨を Github のイシューにコメントしてください。
  8. 最後にわたしたちが、Pygame Zero のドキュメントに新たな翻訳版へのリンクを追加します。

注意 Pygame Zero は随時アップデートされ、それに連れてドキュメントも変更されます。git を使って英語版の変更部分の diff を確認できますから、対応する部分の翻訳を修正してください。