テトリス風落ち物パズルを作る part01 フィールド描画


[先日のネタ出し記事](https://qiita.com/comet725/items/f17b3168eceb627bb669)より

> Hot Soup Processorやpyxel、pygameで作った経験をまとめる
> TSpinやHold等のワールドルールを入れたい

とりあえずテトリス風落ち物パズルを作っていく

# 画像
表示用に以下の画像を用意した
![mino_block.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398956/39a8062a-5a11-b221-c9ec-83ad5293e016.png)

自作画像なので二次配布しないのであれば自由にどうぞ

# フィールドマップ作成

上記の画像を使い Tiledでフィールドを作ってみた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398956/67fe91b8-0ea3-9b95-2a51-1f02b47e7c4f.png)

中央がフィールドで、左がHOLD、右がNEXTのイメージ

~~HOLDの機能作れなかったら左の表示は消します~~

適当にエクスポート
<details>
<summary>出来上がったファイル</summary>

```xml:field.tmx
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="32" height="24" tilewidth="32" tileheight="32" infinite="0" nextlayerid="3" nextobjectid="1">
 <tileset firstgid="1" source="mino_block.tsx"/>
 <layer id="1" name="mainfield" width="32" height="24">
  <data encoding="csv">
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,
1,1,1,33,34,34,34,34,35,1,9,1,1,1,1,1,1,1,1,1,1,9,1,33,34,34,34,34,35,1,1,1,
1,1,1,36,1,1,1,1,37,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,36,1,1,1,1,37,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,38,39,39,39,39,40,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,9,1,36,1,1,1,1,37,1,1,1,
1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,1,38,39,39,39,39,40,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
</data>
 </layer>
</map>

```

これとなんか画像のオブジェクトファイル？みたいのが必要
- 拡張子tsxなのは草、typescriptじゃなくね？

```xml:mino_block.tsx
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.10.2" name="mino_block" tilewidth="32" tileheight="32" tilecount="64" columns="8">
 <image source="mino_block.png" width="256" height="256"/>
</tileset>

```
</details>


# 環境構築
poetryが入っていること前提に環境構築していく

バージョンとかライブラリとか適当に指定
- Python のバージョン: 3.12
- ライブラリ: pygame, pytmx

出来上がったのが以下のファイル
- authorsは自分の名前入れてね

```toml:pyproject.toml
[tool.poetry]
name = "tetris-like"
version = "0.1.0"
description = ""
authors = [""]

[tool.poetry.dependencies]
python = "^3.12"
pygame = "^2.5.2"
pytmx = "^3.32"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```

これ使って`poetry init`とかすれば多分環境作れるはず

# とりあえず描画

インストールしたpygameとpytmxを使ってこのフィールドを描画してみる

```Python:main.py
import pygame
from pytmx import util_pygame as tmx_util

class Game:
    TILE_SIZE = 32
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((1024, 768))

    def load_map(self, filename):
        self.tmx_data = tmx_util.load_pygame(filename)

    def draw_tile(self, x, y, tile_id):
        tile_image = self.tmx_data.get_tile_image_by_gid(tile_id)
        if tile_image:
            self.screen.blit(tile_image, (x * self.TILE_SIZE, y * self.TILE_SIZE))

    def run(self):
        self.map_loaded = False

        clock = pygame.time.Clock()
        running = True

        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

            if not self.map_loaded:
                self.load_map('field.tmx')
                self.map_loaded = True

            self.screen.fill((0, 0, 0))

            for layer in self.tmx_data.layers:
                if layer.name == "mainfield":
                    for x, y, gid in layer.iter_data():
                        self.draw_tile(x, y, gid)

            pygame.display.flip()
            clock.tick(60)

        pygame.quit()

if __name__ == "__main__":
    game = Game()
    game.run()

```

実行してみた結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398956/3428c3cb-1fb7-9abd-ab16-4e820205dec0.png)


正しく表示できていそう

# まとめ

Tiledでフィールドを作りpygame,pytmxを使って描画した

次回: [テトリス風落ち物パズルを作る part02 ミノ配置 #Python - Qiita](https://qiita.com/comet725/items/a4c8a544e49cfb29d5f4)