CODE Python RPG

独学でPythonでRPGを作成する(第29回)マップ画像を取得

投稿日:

こんにちは。
「Pythonでつくる ゲーム開発 入門講座」の
Chapter11,12(本格RPGを作ろう!全編・後編)を基にRPGを作っていきます。

前回まで呪文について変更を加えてきました。
今回は呪文から離れて、マップを変更します。

最初にマップの画像を用意します。
OpenCVを使って、マップチップを切り取って
マップの画像を取得します。

目次

  1. OpenCV
  2. OpenCVの機能
  3. マップチップを切り取る
  4. 切り取った画像を使う
  5. マップの変更
  6. 最後に

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を使って、
マップチップを切り取って
マップの画像を取得しました。

次回もマップを作っていこうと思います。

まとめサイトへ

 

 

【ご注意】
プログラムやデータなどは著作権法により保護されています。
著作者の許諾を得ずに、プログラムおよびデータそのものまたは改変したものを
配布したり販売したりすることはできません。
また、これらを利用して発生した損害などに関して、著作者は一切責任を負いません。

 

 

-CODE, Python, RPG
-, , , , , , , , ,

Copyright© kerublog , 2021 All Rights Reserved Powered by STINGER.