こんにちは。
「Pythonでつくる ゲーム開発 入門講座」の
Chapter11,12(本格RPGを作ろう!全編・後編)を基にRPGを作っていきます。
前回まで呪文について変更を加えてきました。
今回は呪文から離れて、マップを変更します。
最初にマップの画像を用意します。
OpenCVを使って、マップチップを切り取って
マップの画像を取得します。
目次
OpenCV
今回はマップに使う画像を用意します。
その為にマップチップを切り取ります。
マップチップについてはこちら。
画像を切り取るのに何かないか検索していたら
OpenCVが出てきましたので、これを使います。
色々と画像を処理できるオープンソースのライブラリのようです。
Pythonには何でも揃っていますね・・(^_^;)
インストールは以下の通りです。
pip3 install opencv-python
importしてインストールできているかチェック。
import cv2
実行してエラーが出なければ問題なしです。
OpenCVの機能
(他にもたくさんあると思いますが)
今回使うOpenCVの機能です。
画像の読み込み
img = cv2.imread("test/chip.png")
画像の書き込み
cv2.imwrite("test/sample.png", img1)
img1は書き込む画像です。
今回は切り取った画像を指定します。
画像の高さと幅を取得
# 高さ
height = img.shape[0]
# 幅
width = img.shape[1]
以下のようにも書けます。
height, width = img.shape[:2]
画像を切り取る
img[top : bottom, left : right]
(top,left)の位置から(bottom,right)の位置までを切り取ります。
以上の機能を使って、
(0,0)の位置から(100,100)の位置までを
切り取ります。
import os.path
os.chdir(os.path.dirname(os.path.abspath(__file__)))
import cv2
# 画像読み込み
img = cv2.imread("test/btlbg.png")
# 高さと幅
height, width = img.shape[:2]
print(height) # 結果 → 720
print(width) # 結果 → 880
# 高さ0 → 100、幅 0 → 100を切り取る ※(0,0)から(100,100)を切り取る
img1 = img[0 : 100, 0: 100]
# 画像書き込み
cv2.imwrite("test/sample.png", img1)
実行結果です。
btlbg.png(元ファイル)

sample.png

切り取れました!
マップチップを切り取る
OpenCVの機能を使って
マップチップを切り取り、
マップに使う画像を取得します。
今回は緑\('ω')/箱さんのマップチップを
利用させて頂きます。

この画像は縦に16分割、横に20分割します。
サイズは高さが256、幅が320の為、
高さ:256/16分割 → 16
幅:320/20分割 → 16
となり、切り取った後の
画像のサイズは縦、幅ともに16になります。
画像を切り取るプログラムです。
import os.path
os.chdir(os.path.dirname(os.path.abspath(__file__)))
import cv2
# 画像読み込み
img = cv2.imread("test/chip.png")
height, width = img.shape[:2]
w = 16 # 切り取った画像の横のサイズ
h = 16 # 切り取った画像の縦のサイズ
tmp = 0
for i in range(int(width/w)-1): # 横を20分割
for j in range(int(height/h)-1): # 縦を16分割
img1 = img[j*h : (j+1)*h, i*w: (i+1)*w] # [i*w, j*h]〜[(i+1)*w, (j+1)*h] の画像
cv2.imwrite("test/out_sample" + str(tmp) + ".png", img1) # 画像書き込み
tmp += 1
画像を以下のように切り取ります。


この画像が285個できます。

これでマップに使う画像を用意できました(^_^)
切り取った画像を使う
切り取った画像を使ってマップに表示します。
切り取ったマップを全て使うわけではないので
使うマップだけをmapフォルダに移動して
ファイル名を「数字.png」のようにしました。

この画像はmove.pyで使います。
(省略)
imgFloor = [
pygame.image.load("image/floor.png"),
pygame.image.load("image/tbox.png"),
pygame.image.load("image/cocoon.png"),
pygame.image.load("image/stairs.png")
]
# 追加
imgmap = []
for i in range(31):
imgmap.append(pygame.image.load("map/" + str(i) + ".png"))
for i, img in enumerate(imgmap):
imgmap[i] = pygame.transform.scale(img, (80, 80)) # 画像サイズを縦横ともに80にする
用意した30の画像を読み込みます。
画像のファイル名の数字とリストの添字を
一致させています。
imgmap = []
for i in range(31):
imgmap.append(pygame.image.load("map/" + str(i) + ".png"))
後述しますが、画像の表示は以下にします。
bg.blit(imgmap[n_map[dy][dx]], [X, Y])
n_mapは以下のようなリストです
n_maps = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
]
画像のファイル名と
リストimgmapの添字が一致しているので
bg.blit(imgmap[n_map[dy][dx]], [X, Y])
とすると、n_mapに表示させたい画像の数字を
記載することで表示されるようになります。
画像のサイズは16×16ですが、
マップ上は80×80のため、
画像の大きさを変えています。
for i, img in enumerate(imgmap):
imgmap[i] = pygame.transform.scale(img, (80, 80)) # 画像サイズを縦横ともに80にする
scaleについてはこちら。
マップ上のサイズは以下のX,Yなどです。
定数に入れないとダメですね。。
move.py
def draw_map(bg, fnt, player): # ダンジョンを描画する
bg.fill(BLACK)
for y in range(-4, 6):
for x in range(-5, 6):
X = (x+5)*80 # 横のサイズ:80
Y = (y+4)*80 # 縦のサイズ:80
dx = player.x + x
dy = player.y + y
if 0 <= dx and dx < map_w and 0 <= dy and dy < map_h:
bg.blit(imgmap[n_map[dy][dx]], [X, Y])
# マップの数字が変わるのでコメントアウト
# if n_map[dy][dx] <= 3:
# bg.blit(imgmap[n_map[dy][dx]], [X, Y])
# if n_map[dy][dx] == 9:
# bg.blit(imgWall, [X, Y-40])
# if dy >= 1 and n_map[dy-1][dx] == 9:
# bg.blit(imgWall2, [X, Y-80])
if x == 0 and y == 0: # 主人公キャラの表示
bg.blit(player.img[player.a], [X, Y-40])
draw_para(bg, fnt, player) # 主人公の能力を表示
先程、記載した通り画像は以下で表示します。
bg.blit(imgmap[n_map[dy][dx]], [X, Y])
あとで変更するのですが、マップの定義が
変わるので以下をコメントアウトしました。
# if n_map[dy][dx] <= 3:
# bg.blit(imgmap[n_map[dy][dx]], [X, Y])
# if n_map[dy][dx] == 9:
# bg.blit(imgWall, [X, Y-40])
# if dy >= 1 and n_map[dy-1][dx] == 9:
# bg.blit(imgWall2, [X, Y-80])
あと、今までの床とか宝箱の画像は使わないので、
コメントアウトします。
move.py
(省略)
def move_player(key, player): # 主人公の移動
global tmr, food, potion, blazegem, treasure
r = random.randint(0, 99)
if r < 0: # 敵出現(マップのテストの為、敵が出ないようにする)
tmr = 0
return 10
# if n_map[player.y][player.x] == 0: # 何もない床
# # プレイヤーがいる場所(リストimgFloor 0:床 1:宝箱 2:繭 3:階段 -にしたらリストの逆からになるので-4にしたら床になる) →imgmapに変更
# # これがないと連続して敵が出る。下の方で、移動後にいた場所を0にする
# n_map[player.y][player.x] = -16
# r = random.randint(0, 99)
# if r < 0: # 敵出現
# tmr = 0
# return 10
# elif n_map[player.y][player.x] == 1: # 宝箱に載った
# n_map[player.y][player.x] = 0
# treasure = random.choice([0,0,0,1,1,1,1,1,1,2])
# if treasure == 0:
# potion = potion + 1
# if treasure == 1:
# blazegem = blazegem + 1
# if treasure == 2: # ハズレ
# food = int(food/2)
# tmr = 0
# return 3
# elif n_map[player.y][player.x] == 2: # 繭に載った
# n_map[player.y][player.x] = 0
# r = random.randint(0, 99)
# if r < 40: # 食料
# treasure = random.choice([3,3,3,4])
# if treasure == 3:
# food = food + 20
# if treasure == 4:
# food = food + 100
# tmr = 0
# return 3
# return 1
# elif n_map[player.y][player.x] == 3: # 階段に載った
# tmr = 0
# return 2
# 方向キーで上下左右に移動
x = player.x # 移動したかを確認する為に今の位置を保存
y = player.y
(省略)
move.pyの変更は以上です。
マップの変更
先程、少し記載しましたが
マップ(map.py)を変更します。
使う画像が変わる為です。
変更前
(省略)
class Maps():
def get_map(self, num, player):
if num == 0:
maps = [
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
]
player.d = 1 # プレイヤーの向き
player.a = 2 # プライヤーの画像
player.x = 3 # マップの最初のx座標
player.y = 3 # マップの最初のy座標
return maps
変更後(変更途中)
(省略)
class Maps():
def get_map(self, num, player):
if num == 0:
maps = [
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 24, 3, 3, 3, 3, 23, 16, 16, 16, 24, 3, 3, 3, 23, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 24, 3, 3, 3, 3, 3, 3, 3, 23, 20, 24, 3, 3, 3, 3, 3, 3, 3, 3, 23, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 23, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 23, 20, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 16, 16, 16, 16, 16, 20, 20, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]
]
player.d = 1 # プレイヤーの向き
player.a = 2 # プライヤーの画像
player.x = 3 # マップの最初のx座標
player.y = 3 # マップの最初のy座標
return maps
mapsを変えます。
※まだ変更の途中です・・(^_^;)
数字は画像ファイルの数字です。
16がたくさんありますが、
16は以下の赤枠の画像です。

プログラムを実行した結果は以下のような感じで、
引き続きmapsを作って行きます。

という感じで、今回は以上です!(`・ω・´)
最後に
今回はまだ途中ですが、マップを変更しました。
また、その為にOpenCVを使って、
マップチップを切り取って
マップの画像を取得しました。
次回もマップを作っていこうと思います。
【ご注意】
プログラムやデータなどは著作権法により保護されています。
著作者の許諾を得ずに、プログラムおよびデータそのものまたは改変したものを
配布したり販売したりすることはできません。
また、これらを利用して発生した損害などに関して、著作者は一切責任を負いません。