前回の記事ではパックマンの向きが変わるようにしました。
今回は、マップを作成していきます。
マップチップ作成
マップチップを以下のように作成しました。
水平、垂直反転で使い回せるものは省略しましたが、反転を使わずに全部書いた方が楽だったと、作ってみて思いました。
マップデータの定義
2次元リストに各マス目の情報を以下のように持たせることにしました。
1文字目:水平反転の有無(0 か 1)
2文字目:垂直反転の有無(0 か 1)
3文字目:マップチップの番号(左上から16×16ごとに番号を割り当ててある)
self.map = [ [ "000", "000", "000", "000", "000", "000", "000", "002", "000", "000", "000", "000", "000", "000", "000"], [ "000", "005", "105", "000", "005", "105", "000", "014", "000", "005", "105", "000", "005", "105", "000"], [ "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000"], [ "000", "005", "105", "000", "004", "000", "005", "006", "105", "000", "004", "000", "005", "105", "000"], [ "000", "000", "000", "000", "002", "000", "000", "002", "000", "000", "002", "000", "000", "000", "000"], [ "008", "008", "101", "000", "007", "105", "000", "014", "000", "005", "107", "000", "001", "008", "008"], [ "018", "018", "111", "000", "014", "000", "000", "000", "000", "000", "014", "000", "011", "018", "018"], [ "000", "000", "000", "000", "000", "000", "005", "003", "105", "000", "000", "000", "000", "000", "000"], [ "101", "000", "004", "000", "004", "000", "000", "000", "000", "000", "004", "000", "004", "000", "001"], [ "111", "000", "014", "000", "014", "000", "005", "006", "105", "000", "014", "000", "014", "000", "011"], [ "000", "000", "000", "000", "000", "000", "000", "002", "000", "000", "000", "000", "000", "000", "000"], [ "000", "005", "10a", "000", "005", "105", "000", "014", "000", "005", "105", "000", "00a", "105", "000"], [ "000", "000", "002", "000", "000", "000", "000", "000", "000", "000", "000", "000", "002", "000", "000"], [ "101", "000", "014", "000", "004", "000", "005", "003", "105", "000", "004", "000", "014", "000", "001"], [ "109", "000", "000", "000", "002", "000", "000", "000", "000", "000", "002", "000", "000", "000", "009"], ]
自分でも書いていて意味がわからなくなりそうだったので、反転を使わずにマップチップにゴリゴリ書いたほうが楽ですね。
pyxelには標準でタイルマップエディタが入っているので、それを使っても良いでしょう。
Mapクラスの作成・呼び出し
Mapクラスを以下のように作成しました。
インスタンスは生成しないので、defの上に@classmethodと付けておきます。
class Map: SIZE = 16 # チップサイズ CHIP_WIDTH = 16 CHIP_HEIGHT = 1 # マップチップ座標をスクリーン座標に変換 @classmethod def to_screen(cls, i, j): return (i * cls.SIZE, j * cls.SIZE) # マップチップの描画 @classmethod def draw_chip(cls, i, j, val): # スクリーン座標に変換 x, y = cls.to_screen(i, j) # マップチップの番号を取得 no = str(val[2]) no = 10 if no == "a" else int(no) # チップ画像の座標を計算 u = (no % cls.CHIP_WIDTH) * cls.SIZE v = (math.floor(no / cls.CHIP_WIDTH)) * cls.SIZE # チップ反転 w_reverse = -1 if val[0] == "1" else 1 h_reverse = -1 if val[1] == "1" else 1 pyxel.blt(x, y, 1, u, v, cls.SIZE * w_reverse, cls.SIZE * h_reverse, 0)
マップチップの枚数が二桁になったときの処理を考えておらず、作っている途中で10枚目が必要になってしまいました。
そこで、マップデータには10の代わりにaを持たせて、
no = 10 if no == "a" else int(no)
という命令で誤魔化しました。
Appクラスからは以下の関数で呼び出します。
def draw_map(self): # 各チップの描画 for j, arr in enumerate(self.map): for i, d in enumerate(arr): Map.draw_chip(i, j, d)
ソースコード
前回からの主な変更点
- Mapクラスの追加
- Appクラスにマップデータを2次元リストで定義
import pyxel import math WINDOW_H = 240 WINDOW_W = 240 char_H = 16 char_W = 16 # キャラクターのクラスを作成 class Player: def __init__(self, x, y, h): # 表示位置の座標を示す変数 self.x = x self.y = y # 画像の座標を示す変数 self.u = 0 self.h = h # プレイヤーの判別にも使用 # 方向を示す変数 self.up_down = 1 self.left_right = 1 self.rotation = 0 # 画像切り替えの折返し判断 self.sw = 0 # 移動速度 self.speed = 4 # キーバインド if h == 0: #プレイヤー1の設定 self.up = pyxel.KEY_UP self.down = pyxel.KEY_DOWN self.left = pyxel.KEY_LEFT self.right = pyxel.KEY_RIGHT if h == 1: #プレイヤー2の設定 self.up = pyxel.KEY_W self.down = pyxel.KEY_S self.left = pyxel.KEY_A self.right = pyxel.KEY_D if h == 2: #プレイヤー3の設定 pass if h == 3: #プレイヤー4の設定 pass # キャラグラフィック変更 def update(self): if self.sw == 0: self.u += 1 if self.u == 2: self.sw = 1 else: self.u -= 1 if self.u == 0: self.sw = 0 # キャラクター移動 def move(self): if pyxel.btn(self.up): self.y -= self.speed self.up_down = 1 self.rotation = 64 if pyxel.btn(self.down): self.y += self.speed self.up_down = -1 self.rotation = 64 if pyxel.btn(self.left): self.x -= self.speed self.left_right = 1 self.rotation = 0 if pyxel.btn(self.right): self.x += self.speed self.left_right = -1 self.rotation = 0 def draw(self): pyxel.blt(self.x, self.y, 0, self.u * 16, self.h * 16 + self.rotation, char_W * self.left_right, char_H * self.up_down, 0) class Map: SIZE = 16 # チップサイズ CHIP_WIDTH = 16 CHIP_HEIGHT = 1 # マップチップ座標をスクリーン座標に変換 @classmethod def to_screen(cls, i, j): return (i * cls.SIZE, j * cls.SIZE) # マップチップの描画 @classmethod def draw_chip(cls, i, j, val): # スクリーン座標に変換 x, y = cls.to_screen(i, j) # マップチップの番号を取得 no = str(val[2]) no = 10 if no == "a" else int(no) # チップ画像の座標を計算 u = (no % cls.CHIP_WIDTH) * cls.SIZE v = (math.floor(no / cls.CHIP_WIDTH)) * cls.SIZE # チップ反転 w_reverse = -1 if val[0] == "1" else 1 h_reverse = -1 if val[1] == "1" else 1 pyxel.blt(x, y, 1, u, v, cls.SIZE * w_reverse, cls.SIZE * h_reverse, 0) class App: def __init__(self): pyxel.init(WINDOW_W, WINDOW_H, caption="Hello Pyxel") # マップデータの定義 self.map = [ [ "000", "000", "000", "000", "000", "000", "000", "002", "000", "000", "000", "000", "000", "000", "000"], [ "000", "005", "105", "000", "005", "105", "000", "014", "000", "005", "105", "000", "005", "105", "000"], [ "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000", "000"], [ "000", "005", "105", "000", "004", "000", "005", "006", "105", "000", "004", "000", "005", "105", "000"], [ "000", "000", "000", "000", "002", "000", "000", "002", "000", "000", "002", "000", "000", "000", "000"], [ "008", "008", "101", "000", "007", "105", "000", "014", "000", "005", "107", "000", "001", "008", "008"], [ "018", "018", "111", "000", "014", "000", "000", "000", "000", "000", "014", "000", "011", "018", "018"], [ "000", "000", "000", "000", "000", "000", "005", "003", "105", "000", "000", "000", "000", "000", "000"], [ "101", "000", "004", "000", "004", "000", "000", "000", "000", "000", "004", "000", "004", "000", "001"], [ "111", "000", "014", "000", "014", "000", "005", "006", "105", "000", "014", "000", "014", "000", "011"], [ "000", "000", "000", "000", "000", "000", "000", "002", "000", "000", "000", "000", "000", "000", "000"], [ "000", "005", "10a", "000", "005", "105", "000", "014", "000", "005", "105", "000", "00a", "105", "000"], [ "000", "000", "002", "000", "000", "000", "000", "000", "000", "000", "000", "000", "002", "000", "000"], [ "101", "000", "014", "000", "004", "000", "005", "003", "105", "000", "004", "000", "014", "000", "001"], [ "109", "000", "000", "000", "002", "000", "000", "000", "000", "000", "002", "000", "000", "000", "009"], ] pyxel.image(0).load(0, 0, "img/img.png") pyxel.image(1).load(0, 0, "img/stage.png") # フレーム更新時に1加算する変数 self.frame = 0 # Playerクラスのインスタンスを生成、初期位置の座標を引数に渡す self.players = {} self.player_1 = Player( 48, 32, 0) self.player_2 = Player(176, 192, 1) self.player_3 = Player(176, 32, 2) self.player_4 = Player( 48, 192, 3) # 参加するプレイヤーを登録 # [TODO] プレイ人数に応じて変更 self.players = [self.player_1, self.player_2] pyxel.run(self.update, self.draw) def update(self): self.frame += 1 # 3フレームごとにキャラグラフィックを更新 if self.frame % 3 == 0: for player in self.players: player.update() # キャラクターの座標を更新 for player in self.players: player.move() if pyxel.btnp(pyxel.KEY_Q): pyxel.quit() def draw(self): pyxel.cls(0) # マップの描画 self.draw_map() # キャラクターを描画 for player in self.players: player.draw() def draw_map(self): # 各チップの描画 for j, arr in enumerate(self.map): for i, d in enumerate(arr): Map.draw_chip(i, j, d) App()
実行結果
マップが描画されました。
次回は当たり判定を実装していきます。
昔のゲームは容量を削減するためにこういったところで頑張ったらしいですね。