【Pyxel】Pyxelで学ぶゲームプログラム 〜マップデータの読み込み
今回は、レトロゲームエンジン「Pyxel」を使って、マップデータを読み込む方法について紹介します。
■最小限のコード
まずはPyxelで動作させるための最小限のコードを書きます。
import pyxel
class App:
def __init__(self):
pyxel.init(160, 120, fps=60)
pyxel.run(self.update, self.draw)
def update(self):
pass
def draw(self):
pass
App()
実行して何もない真っ黒な画面が表示されることを確認します。
■マップチップ画像のダウンロード
以下のURLからマップチップ画像をダウンロードします。
http://syun777.sakura.ne.jp/tmp/pyxel/tileset.png
そしてダウンロードした画像を実行するスクリプトと同じフォルダに配置します。
■マップチップ画像の読み込みと描画
マップチップ画像を読み込んで表示します。
import pyxel
class App:
def __init__(self):
pyxel.init(160, 120, fps=60)
pyxel.image(0).load(0, 0, "tileset.png") # マップチップ画像の読み込み
pyxel.run(self.update, self.draw)
def update(self):
pass
def draw(self):
pyxel.cls(0)
# 画像を描画する
pyxel.blt(0, 0, 0, 0, 0, 40, 32, 2)
App()
ひとまず以下のように表示されます。
■マップチップ画像の描画
先ほどは画像を描画しただけですので、今度は自由にマップとなるデータを用意して描画できるようにします。
マップ画像は以下のように番号が割り振られているとします。
その場合、以下のデータをマップデータとした場合、
map = [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 5, 7, 0, 0, 0],
[0, 15, 17, 0, 0, 0],
[0, 0, 0, 0, 5, 7],
[0, 0, 0, 0, 15, 17],
]
マップは以下のように描画されることになります。
では、マップチップ画像を読み込んで描画するために、専用のクラスを作ります。
import pyxel
import math # math.floorを使うので必要
class Map:
SIZE = 8 # チップサイズ
CHIP_WIDTH = 5 # 1列に5つ並んでいる
CHIP_HEIGHT = 5 # 5行並んでいる
# マップチップ座標をスクリーン座標に変換
@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)
# チップ画像の座標を計算
u = (val % cls.CHIP_WIDTH) * cls.SIZE
v = (math.floor(val / cls.CHIP_WIDTH)) * cls.SIZE
pyxel.blt(x, y, 0, u, v, cls.SIZE, cls.SIZE, 2)
これを使ってマップを描画します。
class App:
def __init__(self):
pyxel.init(160, 120, fps=60)
# マップデータの定義
self.map = [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 5, 7, 0, 0, 0],
[0, 15, 17, 0, 0, 0],
[0, 0, 0, 0, 5, 7],
[0, 0, 0, 0, 15, 17],
]
pyxel.image(0).load(0, 0, "tileset.png")
pyxel.run(self.update, self.draw)
def update(self):
pass
def draw(self):
pyxel.cls(0)
# マップの描画
self.draw_map()
def draw_map(self):
# 外枠の描画
pyxel.rectb(0, 0, Map.SIZE*6, Map.SIZE*6, 5)
# 各チップの描画
for j, arr in enumerate(self.map):
for i, d in enumerate(arr):
Map.draw_chip(i, j, d)
実行すると以下のように表示されます。
■外部テキストから読み込みしてマップを表示する
プログラム内でマップデータを定義しましたが、これを外部テキストに用意して簡単に編集できるようにします。
まずば、"map.txt" というテキストファイルを作成して、以下のように入力します。
0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0
0, 5, 7, 0, 0, 0
0, 15, 17, 0, 0, 0
0, 0, 0, 0, 5, 7
0, 0, 0, 0, 15, 17
このテキストのマップデータを読み込んで単体で動かすコードは以下の通りです。
# ①マップデータ読み込み
map_file = open("map.txt")
map = []
for line in map_file:
# ②一行ずつ読み込み
# ③"," で文字を区切る
arr = line.split(",")
mapdata = []
for data in arr:
# ④空白文字を削除
v = data.strip()
if v == "":
# 無効なデータがあればおしまい
break
# ⑤マップデータに追加
mapdata.append(int(v))
# ⑥1行分を登録
map.append(mapdata)
print(map)
これを元にマップデータのロード処理を書きます。
まずは、load_map() を実装します。
def load_map(self, txt):
# マップ読み込み
map = []
map_file = open(txt)
for line in map_file:
# 1行ずつ読み込み
array = []
data = line.split(",")
for d in data:
# 余分な文字を削除
s = d.strip()
if s == "":
break
v = int(d.strip())
array.append(v)
map.append(array)
return map
__init__() に読み込み処理を追加します。
def __init__(self):
pyxel.init(160, 120, fps=60)
# マップデータ読み込み
self.map = self.load_map("map.txt")
pyxel.image(0).load(0, 0, "tileset.png")
pyxel.run(self.update, self.draw)
実行して前に表示した状態と変わっていないことを確認します。
■プレイヤー表示
プレイヤー(ニャンコ)を表示します。
まずは、"map.txt" を編集して一番右上に "8" を記入します。
0, 0, 0, 0, 0, 8
0, 0, 0, 0, 0, 0
0, 5, 7, 0, 0, 0
0, 15, 17, 0, 0, 0
0, 0, 0, 0, 5, 7
0, 0, 0, 0, 15, 17
プレイヤーとなるニャンコはマップチップ画像上、 "8" 番目にいるので、 "8" をマップデータに指定することとなります。
8をプレイヤーの開始地点とするために、指定の番号を探す、search_map() を実装します。
def search_map(self, val):
# 指定の値が存在する座標を返す
for j, arr in enumerate(self.map):
for i, v in enumerate(arr):
if v == val:
# 見つかった
return i, j
# 見つからなかったら (-1, -1) を返す
return -1, -1
さらに、マップに直接値を設定できる、set_map() を実装します。
def set_map(self, i, j, val):
# 指定の位置に値を設定する
self.map[j][i] = val
__init__() でプレイヤーの初期座標を設定する
def __init__(self):
pyxel.init(160, 120, fps=60)
# マップデータ読み込み
self.map = self.load_map("map.txt")
# プレイヤーの位置を取得
self.x, self.y = self.search_map(8)
# マップデータからプレイヤーを削除
self.set_map(self.x, self.y, 0)
pyxel.image(0).load(0, 0, "tileset.png")
pyxel.run(self.update, self.draw)
draw_player() を実装してプレイヤーを描画します。
# プレイヤーの描画
def draw_player(self):
Map.draw_chip(self.x, self.y, 8)
そして、draw() でプレイヤー描画関数を呼び出すようにします。
def draw(self):
pyxel.cls(0)
# マップの描画
self.draw_map()
# プレイヤーの描画
self.draw_player()
実行するとプレイヤー(ニャンコ)が描画されるようになりました。
■全てのソースコード
ここまでのコードは以下の通りとなります。
import pyxel
import math # math.floorを使うので必要
class Map:
SIZE = 8 # チップサイズ
CHIP_WIDTH = 5 # 1列に5つ並んでいる
CHIP_HEIGHT = 5 # 5行並んでいる
# マップチップ座標をスクリーン座標に変換
@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)
# チップ画像の座標を計算
u = (val % cls.CHIP_WIDTH) * cls.SIZE
v = (math.floor(val / cls.CHIP_WIDTH)) * cls.SIZE
pyxel.blt(x, y, 0, u, v, cls.SIZE, cls.SIZE, 2)
class App:
def __init__(self):
pyxel.init(160, 120, fps=60)
# マップデータ読み込み
self.map = self.load_map("map.txt")
# プレイヤーの位置を取得
self.x, self.y = self.search_map(8)
# マップデータからプレイヤーを削除
self.set_map(self.x, self.y, 0)
pyxel.image(0).load(0, 0, "tileset.png")
pyxel.run(self.update, self.draw)
def load_map(self, txt):
# マップ読み込み
map = []
map_file = open(txt)
for line in map_file:
# 1行ずつ読み込み
array = []
data = line.split(",")
for d in data:
# 余分な文字を削除
s = d.strip()
if s == "":
break
v = int(d.strip())
array.append(v)
map.append(array)
return map
def search_map(self, val):
# 指定の値が存在する座標を返す
for j, arr in enumerate(self.map):
for i, v in enumerate(arr):
if v == val:
# 見つかった
return i, j
def set_map(self, i, j, val):
# 指定の位置に値を設定する
self.map[j][i] = val
def update(self):
pass
def draw(self):
pyxel.cls(0)
# マップの描画
self.draw_map()
# プレイヤーの描画
self.draw_player()
# プレイヤーの描画
def draw_player(self):
Map.draw_chip(self.x, self.y, 8)
def draw_map(self):
# 外枠の描画
pyxel.rectb(0, 0, Map.SIZE*6, Map.SIZE*6, 5)
# 各チップの描画
for j, arr in enumerate(self.map):
for i, d in enumerate(arr):
Map.draw_chip(i, j, d)
App()
■全てのデータのダウンロード
どうしても動作しない方は、以下から今回使用したデータをダウンロードして動作を確認してみてください。
http://syun777.sakura.ne.jp/tmp/pyxel/tilemap.zip
今回はここまでとします。
次回で、ニャンコの移動とゲームクリアを実装します。
続きを書きました。アイテムの回収とゲームクリア処理の実装についてとなります。
この記事が気に入ったらサポートをしてみませんか?