マップファイル
ファイルからマップを読み込んでみます。基本的にRPG編のマップのロード(2008/6/1)とほとんど同じです。
Mapクラス
マップを表すMapというクラスを新たに追加します。Mapクラスはマップ内のプレイヤースプライト、まだ登場してませんが敵スプライト、コインなどのアイテムスプライトも合わせて管理することにします。つまり、Mapオブジェクトを更新したり描画すれば内部のスプライトもいっしょに更新、描画されます。
class Map: """マップ(プレイヤーや内部のスプライトを含む)""" GS = 32 # グリッドサイズ def __init__(self, filename): # スプライトグループの登録 self.all = pygame.sprite.RenderUpdates() self.blocks = pygame.sprite.Group() Python.containers = self.all Block.containers = self.all, self.blocks # プレイヤーの作成 self.python = Python((300,200), self.blocks) # マップをロードしてマップ内スプライトの作成 self.load(filename) # マップサーフェイスを作成 self.surface = pygame.Surface((self.col*self.GS, self.row*self.GS)).convert() def draw(self): """マップサーフェイスにマップ内スプライトを描画""" self.surface.fill((0,0,0)) self.all.draw(self.surface) def update(self): """マップ内スプライトを更新""" self.all.update() def load(self, filename): """マップをロードしてスプライトを作成""" map = [] fp = open(filename, "r") for line in fp: line = line.rstrip() # 改行除去 map.append(list(line)) self.row = len(map) self.col = len(map[0]) self.width = self.col * self.GS self.height = self.row * self.GS fp.close() # マップからスプライトを作成 for i in range(self.row): for j in range(self.col): if map[i][j] == 'B': Block((j*self.GS, i*self.GS)) # ブロック
まず、__init__()ですが、今までPyActionクラスに書いていたプレイヤーpythonの作成やスプライトグループの登録処理がここに移っています。load()はマップファイルからマップ情報をロードし、ブロックなどのスプライトを作成します。マップサーフェイスはマップやスプライトを描画するサーフェイスです。このサーフェイスはマップの大きさと同じ広さが確保されています。マップ内部のスプライトはdraw()でこのサーフェイスに描画してます。
# Mapクラスのdraw()メソッド def draw(self): """マップサーフェイスにマップ内スプライトを描画""" self.surface.fill((0,0,0)) self.all.draw(self.surface)
サーフェイスに描画しただけでは画面には表示されません。PyActionクラスのdraw()でマップサーフェイスを画面に描画しています。
# PyActiomクラスのdraw()メソッド def draw(self, screen): # スプライトをマップサーフェイスに描画 # この時点では画面に表示されない self.map.draw() # マップサーフェイスをスクリーンに描画 screen.blit(self.map.surface, (0,0), (0,0,SCR_RECT.width, SCR_RECT.height))
draw()がPyActionとMapに2つありますがしっかり区別してください。
- Mapのdraw()はマップ内部のスプライトをマップサーフェイスに描画します。
- PyActionのdraw()はマップサーフェイスをスクリーン(画面)に描画します。
なんでこんな面倒くさいことをやっているかというとあとでマップスクロール機能を追加するためです。Mapのdraw()ではスプライトを含むマップ全体をマップサーフェイスに描画し、PyActionのdraw()ではスクロールに合わせてマップサーフェイスの一部だけ画面に描画します。これは次回取り上げます。
(注)これはdraw()が2回発生するのでかなり効率が悪いかも。それともblit()は転送するだけだからOK???
マップファイル
マップファイルは下のような感じです。Bがブロックを表していて、空白は何もないことを表しています。このファイルを読み込んでBがあったらその位置にBlockオブジェクトを作成します。あとでアイテムや敵キャラを追加するときはこのファイルにCやPなどの文字を追加して初期位置を指定します。
(注)等幅フォントでないと崩れます
BBBBBBBBBBBBBBBBBBBB B B B B B BBBBBBBBBBB B B B B B B BBBBBBBB B B B B B B B B B BB B BBB BB BBBBBB BBBB B BBBBB BBBBBBBBBBBBBBBBBBBB
マップのロード
アクションゲームを構成するブロックや敵キャラなどは全部スプライトとして扱います。ブロックは固定のマップと違って壊せるようにしたかったためです。
(注)一般的なアクションゲームがどういう作りになっているのかちょっとわかりません。もしかしたらすごく効率が悪い方法かもしれません。重すぎて動かないか?と思ったのですがそうでもありませんでした。
マップファイルのロードはload()です。
def load(self, filename): """マップをロードしてスプライトを作成""" map = [] fp = open(filename, "r") for line in fp: line = line.rstrip() # 改行除去 map.append(list(line)) self.row = len(map) self.col = len(map[0]) self.width = self.col * self.GS self.height = self.row * self.GS fp.close() # マップからスプライトを作成 for i in range(self.row): for j in range(self.col): if map[i][j] == 'B': Block((j*self.GS, i*self.GS)) # ブロック
ファイルから文字を読み込んでBとう文字が見つかったら位置を指定してBlockオブジェクトを作成しています。作成されたブロックオブジェクトは自動的にデフォルトスプライトグループであるself.allに追加されます。self.allはマップクラスのupdate()で更新され、 draw()で描画されます。これで前回までと同じようなマップができました。次回はさらに大きなマップを作成してスクロールできるようにしてみます。