Pygame Zero⑤マップの作成

Image from Gyazo

マップの作成

2Dゲームでは画像をタイル状に表示することで,ゲーム・マップを作成する方法が採用されています. タイル状の画像を進行方向とは逆にシフトさせることで,ゲーム画面のスクロールも表現できます. Pygame Zeroでは,マップを作成・表示するためのクラスは用意されていませんので,独自に Mapクラス を実装します. また,これまで取り上げてこなかった ディクショナリ と呼ばれるデータ構造も紹介します. ディクショナリは,リストと同様に,複数の値を記憶するために用います. if文で表現される条件分岐の代替として用いることが可能であり,状況に応じて,リストと使い分けると効果的です.

準備

Muエディタを起動したら,Pygame Zero モードを選択しましょう. 保存用のフォルダを「chapter12」という名前で作成し,ソースファイルを「game.py」という名前でフォルダ内に保存します. また,ゲームを表示するためのウィンドウを作成します. ウィンドウの幅は640px,高さは480px,背景色は白色に設定します.

WIDTH = 640
HEIGHT = 480

def draw():
	screen.fill("white")

ディクショナリ

ここでは,Pythonのデータ構造の一つであるディクショナリ(辞書)について確認しましょう. リストとの違いを意識すると良いです.

ディクショナリの使い方

リストは,0,1,2などの参照番号で,記憶させた要素を参照する仕組みでした. 一方,ディクショナリは,任意のキーで,記憶させた要素を参照する仕組みです. 数値だけでなく,文字列もキーとして指定が可能です. ここでは,“a”,“b”,“c”,をキーとして指定しています. ディクショナリを作成するときは,キーとバリュー(記憶させる値)の組み合わせを,{}で括って列挙します. しかし,データを参照するときは,リストと同様に[]を用いることに注意してください.

dic = {"a": "apple", "b": "banana", "c": "cherry"}  # ディクショナリの作成
print(dic)  # -> {'a': 'apple', 'b': 'banana', 'c': 'cherry'}
print(dic["a"])  # -> apple
print(dic["b"])  # -> banana
print(dic["c"])  # -> cherry

Image from Gyazo

条件分岐としてのディクショナリ

ディクショナリの構造を利用して,条件分岐を表現することができます. 例えば,変数xの値に応じて,3種類に分岐するif文は次のように記述できます.

x = "b"  # xの値に応じて分岐

if x == "a":
    print("apple")
elif x == "b":
    print("banana") # -> banana
elif x == "c":
    print("cherry")

これを,ディクショナリを用いて表現すると次のように記述できます. キーが条件として機能していることがわかります. if文よりもスッキリと表現できることが多いので活用しましょう.

x = "b"
print(dic[x])  # -> banana

マップ

2Dゲームにおいて,マップを表現する方法の一つが,画像をタイル状に並べる方法です. ここでは,自由にマップをデザインするために,独自にMapクラスを定義してみます.

Mapの仕組み

マップの素材として,KENNYRGB Baseで提供されている次の4種類のタイル画像を用います. それぞれ,平地,海,道路,草を表しており,画像サイズは32ピクセル×32ピクセルです. 画像ファイルをダウンロードしたら,imagesフォルダにコピーしてください.

Image from Gyazo

Image from Gyazo

Image from Gyazo

Image from Gyazo

ディクショナリmap_dicを定義し,特定の文字とファイル画像名の,キー・バリューを登録します. 例えば,fという文字に対し,tile0という画像ファイル名がペアとなっています. これを,条件分岐の代替として用います.

map_dic = {
    "f": "tile0",  # 森
    "s": "tile1",  # 海
    "r": "tile2",  # 道路
    "g": "tile3",  # 草
}

タイル画像をスクリーンにタイル状に敷き詰めます. ここでは,説明のため,4枚×3枚のタイルを敷き詰めることを考えます. 4×3の2重構造のリストを生成し,表示するタイル画像に対応する文字(f,s,r,g)を設定しておきます. スクリーンに描画する際には,この文字に対応する画像ファイルを,ディクショナリmap_dicを用いて選択します.

Image from Gyazo

画像サイズが32ピクセル×32ピクセルであるのに対し, 実際のスクリーンのサイズは640ピクセル×480ピクセルです. このため,20枚×15枚のタイル画像を並べることになります. 次に示すmap_dataが20×15の2重構造のリストです.

map_data = [
    ["s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s"],
    ["f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f"],
    ["f", "f", "g", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "g", "f", "f", "f", "f"],
    ["f", "f", "f", "f", "f", "g", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "g", "f"],
    ["g", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "g", "f", "f", "f", "f", "f", "f", "f", "f"],
    ["f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f"],
    ["r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r"],
    ["r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r"],
    ["r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r"],
    ["f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f"],
    ["g", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "g", "f", "f"],
    ["f", "f", "g", "f", "f", "f", "f", "f", "g", "f", "f", "g", "f", "f", "g", "f", "f", "f", "f", "f"],
    ["f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "g", "f"],
    ["f", "f", "f", "f", "f", "g", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f", "f"],
    ["s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s", "s"]
]

Mapクラス

上記のディクショナリmap_dicと,2重構造のリストmap_dataを用いて,マップを表示する Mapクラス を実装します. コンストラクタでは,map_dicmap_dataと,タイル画像のサイズtile_sizeを引数として渡します. また,draw()を新たに定義し,文字に対応する画像ファイルをscreen.blit()で描画します.

class Map:

	# コンストラクタ
    def __init__(self, map_dic, map_data, tile_size):
        self.map_dic = map_dic
        self.map_data = map_data
        self.tile_size = tile_size

    def draw(self):
        max_i = len(map_data[0])  # -> 20
        max_j = len(map_data)  # -> 15

        for i in range(max_i):
            for j in range(max_j):
                number = self.map_data[j][i]  # タイル画像に対応する文字を取得
                image = self.map_dic[number]  # タイル画像のファイル名を取得
                screen.blit(image, (i * self.tile_size, j * self.tile_size))

Mapクラスを定義したら,オブジェクトmapを生成し,draw()を実行します. タイルのサイズには$32$ピクセルを指定します.

# オブジェクトの生成
map = Map(map_dic, map_data, 32)

def draw():
    screen.fill("white")
    map.draw() # マップの描画

Image from Gyazo

画面のスクロール

タイル画像を左にシフトさせることで,ゲーム画面をスクロールします. 最初に,自動車の画像をダウンロードして,imagesフォルダにコピーしてください. この自動車の画像をゲーム画面の中央に配置することで, スクロールで自動車が前進しているように錯覚させることができます.

car.png

Image from Gyazo

Actorクラスで,自動車のスプライトを生成し,ゲーム画面の中央に配置します. このとき,自動車が右方向を向くように,時計周りに90°回転させておきます.

# 自動車のスプライト
car = Actor("car", center=(WIDTH/2, HEIGHT/2))
car.angle = -90  # 時計周りに90°回転

def draw():
    screen.fill("white")
    map.draw()
    car.draw()  # 自動車のスプライトを表示

Image from Gyazo

Mapクラスに,スクロールのためのshift()を定義します. リストの先頭の要素を取り出し,リストの最後尾に移動させます. これにより,マップが左に1タイル分だけ,移動することになります.

# マップを左にシフト(スクロール)
def shift(self):
        for list in self.map_data:
            head = list.pop(0)  # リストの先頭要素を取り出し
            list.append(head)  # リストの差後尾に先頭要素を追加

clock.schedule_interval()を利用して,上記のshift()を0.2秒ごとに繰り返し実行します.

# 0.2秒ごとにmap.shift()を実行する
clock.schedule_interval(map.shift, 0.2)

Image from Gyazo

拡大・縮小・反転(補足)

Pygame Zero Helperを利用すると キャラクター(Actorクラス)の拡大・縮小・反転が可能です. 上記のサイトからpgzhelper.pyをダウンロードし,プロジェクトのフォルダ内に配置してください. また,game.pyの冒頭に下記のコードを追加してください.

from pgzhelper import *

キャラクターを拡大・縮小するにはscaleを利用します.

car = Actor("car.png", center=(WIDTH/2, HEIGHT/2))
car.scale = 2.0 # 2倍

キャラクターを左右反転するにはflip_x,上下反転するにはflip_yを利用します.

car = Actor("car.png", center=(WIDTH/2, HEIGHT/2))
car.flip_x = -1.0 # 左右反転

キャラクターを前進するにはmove_forward(),後退するにはmove_back()を利用します.

car = Actor("car.png", center=(WIDTH/2, HEIGHT/2))
car.move_forward() # 前進

課題

次の課題に取組んでください.

Image from Gyazo

課題を完成させたらスクリプトを保存し,chapter12フォルダをZIPで圧縮してから,chapter12.zipという名前でファイルを提出してください.

参考書籍

愛知県名古屋市にある椙山女学園大学 文化情報学部 向研究室の公式サイトです. 専門は情報科学であり,人工知能やデータベースなどの技術要素を指導しています. この公式サイトでは,授業で使用している教材を公開すると共に, ベールに包まれた女子大教員のミステリアスな日常を4コマ漫画でお伝えしていきます. サイトに関するご意見やご質問はFacebookまたはTwitterでお問い合わせください.