マップを作る

この記事はaidiaryさんのコードを参考にして書かれてます。

[記事全体の目次へ移動](https://qiita.com/hijpa/items/056acaa13e935590fdc2)

# リストでマップを作る

<details><summary>ここをクリックしてください</summary>

![マップ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874121/90459c93-e06a-f124-e9c2-cae60ce7dca8.png)

```python:
import pygame
from pygame.locals import *
import sys
import os
 
COL = 3  # マップ全体のヨコのサイズ(３マス)
GS = 32  # ひとつのマスのサイズ（ピクセル）

map_list = [1,0,1]# 0:草地
       
def draw_map(screen):
    """マップを描画する"""
    for c in range(COL):
        if map_list[c] == 0:
            screen.blit(grassImg, (c*GS, 0))
        elif map_list[c] == 1:
            screen.blit(seaImg, (c*GS, 0))
            
width, height = 96, 32 
pygame.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("マップを作る")
 
# 草原の画像をロード
grassImg = pygame.image.load(パス)
# 海の画像をロード
seaImg = pygame.image.load(パス)
 
clock = pygame.time.Clock()
running = True
while running:
    clock.tick(60)
    draw_map(screen)  # マップ描画
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

```

COL = 3
マップ全体の列数です。ピクセル単位ではなく、グリッドの数です。

GS = 32
ひとつのマスのサイズ（ピクセル単位）です。


map_list = [1, 0, 1]
マップを描き分けるためのリストです。0は草地、1は海を表します。

def draw_map(screen):
マップを描画する

for c in range(COL):
繰り返し処理です。COLは3なので、0～2の数字を作ります。        

if map_list[c] == 0:
リストのインデックスに合わせて、描画を判定します。
map_listのインデックスが1のとき、数字が0なので描画します。

screen.blit(grassImg, (c * GS, 0))
インデックスが1のとき、c * GSは1 * 32なので、描画する座標は(32, 0)で真ん中です。

elif map_list[c] == 1:
map_listのインデックスが0と2のとき、数字が1なので描画します。


screen.blit(seaImg, (c * GS, 0))
インデックスが0のとき、c * GSは0 * 32なので、描画する座標は(0, 0)で左端です。
インデックスが2のとき、c * GSは2 * 32なので、描画する座標は(64, 0)で右端です。

</details>

# ２次元配列でマップを作る

<details><summary>ここをクリックしてください</summary>

![スクリーンショット (822).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874121/c56e6328-fcd2-2b41-4458-51ade754849b.png)


```python:
import pygame
from pygame.locals import *
import sys
import os

ROW = 3  # マップ全体のタテのサイズ(３マス)
COL = 3  # マップ全体のヨコのサイズ(３マス)
GS = 32  # ひとつのマスのサイズ（ピクセル）

map_array2d = [[1,1,1],
            [1,0,1],
            [1,1,1]]
       
def draw_map(screen):
    """マップを描画する"""
    for r in range(ROW):
        for c in range(COL):
            if map_array2d[r][c] == 0:
                screen.blit(grassImg, (c*GS, r*GS))
            elif map_array2d[r][c] == 1:
                screen.blit(seaImg, (c*GS, r*GS))
            
width, height = 96, 96 
pygame.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("マップを作る")
 
# 草原の画像をロード
grassImg = pygame.image.load(パス)
# 海の画像をロード
seaImg = pygame.image.load(パス)
 
clock = pygame.time.Clock()
running = True
while running:
    clock.tick(60)
    draw_map(screen)  # マップ描画
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

```

map_array2d = [[1,1,1],
            [1,0,1],
            [1,1,1]]

変数名がちょっとややこしいですが、array2dは２次元配列のことです。
map()という関数があるようなので付け足してみました。

２次元配列はリストの中にリストが入っていると解釈して大丈夫だと思います。

for r in range(ROW):
    for c in range(COL):

2重のループです。
まず１行目を横に描画していって、
２行目、３行目と描画していく感じです。

if map_array2d[r][c] == 0:

２次元配列の行と列のインデックスを指定して、判定しています。
繰り返し処理でこんな風に変化していきます。
map_array2d[0][0],map_array2d[0][1],map_array2d[0][2],
map_array2d[1][0],map_array2d[1][1],map_array2d[1][2],
map_array2d[2][0],map_array2d[2][1],map_array2d[2][2],

![array2d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874121/dc74533e-2ad4-337e-2e5f-d9e483346ae0.png)


</details>

# リストに画像を格納してマップを作る

<details><summary>ここをクリックしてください</summary>

```python:
import pygame
from pygame.locals import *
import sys
import os

ROW = 3  # マップ全体のタテのサイズ(３マス)
COL = 3  # マップ全体のヨコのサイズ(３マス)
GS = 32  # ひとつのマスのサイズ（ピクセル）

map_array2d = [[1,1,1],
            [1,0,1],
            [1,1,1]]
       
def draw_map(screen):
    """マップを描画する"""
    for r in range(ROW):
        for c in range(COL):
            screen.blit(map_list[map_array2d[r][c]], (c*GS, r*GS))
            
width, height = 96, 96 
pygame.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("マップを作る")
 
# 草原の画像をロード
grassImg = pygame.image.load(パス)
# 海の画像をロード
seaImg = pygame.image.load(パス)

map_list = []

map_list.append(grassImg)
map_list.append(seaImg)

clock = pygame.time.Clock()
running = True
while running:
    clock.tick(60)
    draw_map(screen)  # マップ描画
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
```

#草原の画像をロード
grassImg = pygame.image.load(パス)
#海の画像をロード
seaImg = pygame.image.load(パス)

map_list = []

map_list.append(grassImg)
map_list.append(seaImg)

画像データをリストに入れてます。


map_array2d = [[1,1,1],
            [1,0,1],
            [1,1,1]]

def draw_map(screen):
    """マップを描画する"""
    for r in range(ROW):
        for c in range(COL):
            screen.blit(map_list[map_array2d[r][c]], (c*GS, r*GS))

map_array2dのインデックスを指定して要素取得して、それをmap_listのインデックスとして使用して、
画像データを取得してます。

![array2d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874121/4de480d1-b07b-a345-045e-972dc1bc0b51.png)


具体例を示すと、map_array2d[0][0]は２次元配列の左上なので、１です。
map_listには2つの画像が格納されてます。草原の画像が先にリストに追加されてますので、インデックス0は草原の画像で、インデックス1が海の画像です。
map_array2d[0][0]は１なので、海の画像が描画されます。


</details>

# テキストファイルをロードしてマップを作る

<details><summary>ここをクリックしてください</summary>

pythonスクリプトと同じところにdataフォルダを用意する必要があります。
dataフォルダには草原の画像、海の画像を入れます。どちらともpngファイルです。
さらにtxtファイルを入れてください。

![テキストファイル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874121/4435404f-6571-5d86-66e5-ffcac8c9af9a.png)

１行目が行数と列数です。

![スクリーンショット (822).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874121/c03f7171-fff1-ce9d-530b-567a0ed93ede.png)




```python:

import pygame
from pygame.locals import *
import sys
import os

width, height = 96, 96
GS = 32

def main():
    pygame.init()
    screen = pygame.display.set_mode((width, height))
    pygame.display.set_caption("マップのロード")
    # マップチップをロード
    Map.images[0] = load_image("grass")  # 草地
    Map.images[1] = load_image("sea")  # 海
    # マップの作成
    map = Map("マップ")
    clock = pygame.time.Clock()
    running = True
    while running:
        clock.tick(60)
        map.draw(screen)
        pygame.display.update()
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

def load_image(filename):
    filename = os.path.join("data", filename + ".png")
    image = pygame.image.load(filename)
    image = image.convert()
    return image

class Map:
    images = [None] * 2  
    def __init__(self, name):
        self.name = name
        self.row = -1  # 行数
        self.col = -1  # 列数
        self.map = []  # マップデータ（2次元リスト）
        self.load()
    def draw(self, screen):
        """マップを描画する"""
        for r in range(self.row):
            for c in range(self.col):
                screen.blit(self.images[self.map[r][c]], (c*GS,r*GS))
    def load(self):
        """ファイルからマップをロード"""
        file = os.path.join("data", self.name + ".txt")
        # テキスト形式のマップを読み込む
        with open(file) as fp:
            lines = fp.readlines()
            row_str, col_str = lines[0].split()  # 行数と列数
            self.row, self.col = int(row_str), int(col_str)
            for line in lines[1:]:
                line = line.rstrip()
                line = list(line)
                self.map.append([int(x) for x in line])

if __name__ == "__main__":
    main()

```

Map.images[0] = load_image("grass")  # 草地
Map.images[1] = load_image("sea")  # 海

load_image関数を使って画像をロードした後、インデックスを指定してリストに入れています。
("grass") は草原の画像のファイル名と一致させてください。
("sea")は海の画像のファイル名と一致させてください。
違うファイル名を使いたいときは、書き換えてください。

map = Map("マップ")

インスタンスを生成しています。引数の"マップ"はファイル名です。

map.draw(screen)

マップの描画です。

def load_image(filename):
    filename = os.path.join("data", filename + ".png")
    image = pygame.image.load(filename)
    image = image.convert()
    return image

マップ画像をロードする関数です。
2行目は、フォルダ名とファイル名を区切り文字をつけて連結してます。
ここでは相対パスが使われています。dataフォルダはpythonスクリプトと同じところにないといけません。
４行目では画像を最適化しています。ゲーム内で多くの画像を頻繁に描画する場合、このような最適化は非常に重要です。

class Map:
    images = [None] * 2  # マップチップ（番号->イメージ）
    def __init__(self, name):
        self.name = name
        self.row = -1  # 行数
        self.col = -1  # 列数
        self.map = []  # マップデータ（2次元リスト）
        self.load()

２行目でリスト内のNoneを２つにしています。[None,None]
このNoneは画像データに置き換えられます。
このコードでは、画像データが２つなのでNoneも２つですが、画像データの種類が増えるとNoneも増やす必要があります。
self.nameはファイル名です。
self.rowとself.colは行数と列数です。ここではまだ設定されてません。
self.mapはリストを入れるためのリストです。２次元配列のためです。
self.load()はインスタンス生成時にdef loadを実行するためにここに書いてあります。

![スクリーンショット (823).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874121/f4428aef-14f3-5b1c-87d1-308f1a3d5620.png)


def load(self):
    """ファイルからマップをロード"""
    file = os.path.join("data", self.name + ".txt")
    # テキスト形式のマップを読み込む
with open(file) as fp
    lines = fp.readlines()
    row_str, col_str = lines[0].split()  # 行数と列数
    self.row, self.col = int(row_str), int(col_str)
    for line in lines[1:]:
        line = line.rstrip()
        line = list(line)
        self.map.append([int(x) for x in line])

テキストファイルをロードするメソッドです。２次元配列のためです。
with open(file) as fp
withは自動でファイルを閉じるためです。
open()でファイルを開きます。fileはファイルのパスです。
as fp で変数に割り当てています。
readlines()で行ごとに読み込みます。各行がひとつの文字列になります。文字列はリストに入ってます。改行文字のある状態です。
['3 3\n', '111\n', '101\n', '111']
row_str, col_str = lines[0].split()  # 行数と列数
lines[0],つまり'3 3\n'をインデックスで指定して、文字列を分割してリストに入れています。
.split()は引数に区切り文字を指定しますが、からの場合はデフォルトでスペース，改行文字などを区切り文字にします。
'3 3\n'には、3と3の間にスペースがあります。
['3', '3']

これがアンパッキングされているので、各変数に文字列の'3'が格納されています。
self.row, self.col = int(row_str), int(col_str)
int()で、各変数が整数の3になっています。

for line in lines[1:]:
    line = line.rstrip()
    line = list(line)
    self.map.append([int(x) for x in line])

linesリストのインデックス１から３まで処理を繰り返します。
line = line.rstrip()で行末の改行文字を削除します。
'111\n'が'111'になります。
list(line)で反復可能なオブジェクトをリストにしています。
["1","1","1"]

[int(x) for x in line]リスト内法表記で文字列の'1'を整数にしています。
[1,1,1]
self.map.append()でリストに格納されています。
[[1,1,1]]
全ての処理が終わるとこうなります。
[[1, 1, 1], [1, 0, 1], [1, 1, 1]]
２次元配列のできあがりです。改行したものとは見た目が異なりますが、内容は全く同じです。









    


</details>

#

<details><summary>ここをクリックしてください</summary>

```python:


```
</details>

#

<details><summary>ここをクリックしてください</summary>

```python:


```
</details>