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の使い方を学んでください。