「pyxel」に今来た私が試したこと（ChatGPT先生と共に）

# はじめに
この記事は、[pyxel Advent Calendar 2024](https://qiita.com/advent-calendar/2024/pyxel) の6日目の記事となります。

アドベントカレンダのリストを見ていて、今、Pyxelに来た者の記録です。
ChatGPT先生と共にPxcelで簡単なゲームを作ってみたいと思います。

ChatGPT 4oを使用する。

# ChatGPTはPyxelを知っているか

ChatGPTはPyxelを知っているか聞いてみる。

![スクリーンショット 2024-12-22 15.52.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/98ba8a68-28c4-906e-af92-63cbfba9c1f0.png)
![スクリーンショット 2024-12-22 15.52.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/548c762d-981b-a4c9-c11b-cd7b94d38bf7.png)

ChatGPT先生、Pyxelをご存知の様子。
それではやり方を教えてもらおう

# やり方を教えてもらう

![スクリーンショット 2024-12-22 15.52.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/2bf351eb-2fa7-24ce-b3a0-3d0c502d0812.png)
![スクリーンショット 2024-12-22 15.52.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/98ec4e23-c257-122b-42b8-b1d022a5c14d.png)
![スクリーンショット 2024-12-22 15.52.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/5d34d20d-1f27-b659-f656-a012bec69eb4.png)


> pip install pyxel

ね。OK。インストールできた！。


![スクリーンショット 2024-12-22 15.52.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/0b0c21a7-3765-46d3-60e9-6b2c5161c7fd.png)

```test.py
import pyxel

def update():
    # キャラクターや画面の動きをここで書く
    pass

def draw():
    pyxel.cls(0)  # 背景を黒にする
    pyxel.text(50, 50, "Hello, Pyxel!", 7)  # 画面に文字を表示

# 画面の大きさを160x120に設定してゲームを開始
pyxel.init(160, 120)
pyxel.run(update, draw)
```

![スクリーンショット 2024-12-22 15.59.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/54306a40-d3a3-cfca-63d8-694f4f389065.png)

OK。わかったやってみる。

![スクリーンショット 2024-12-22 15.58.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/012bd807-9875-0050-62d3-b28057ddedc5.png)

おお。できた！

# アクションゲームを作ってもらう

![スクリーンショット 2024-12-22 16.00.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/34fc3982-9c83-411e-7141-c35f4cff7532.png)
![スクリーンショット 2024-12-22 16.00.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/134af79c-3157-4d12-43ef-f1ccbef0857b.png)

```game.py
import pyxel
import random

class SimpleActionGame:
    def __init__(self):
        pyxel.init(160, 120, title="Simple Action Game")
        self.player_x = 75
        self.player_y = 100
        self.enemy_x = random.randint(0, 160)
        self.enemy_y = 0
        self.score = 0
        pyxel.run(self.update, self.draw)

    def update(self):
        # プレイヤーの操作
        if pyxel.btn(pyxel.KEY_LEFT):
            self.player_x = max(self.player_x - 2, 0)
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.player_x = min(self.player_x + 2, 160 - 8)
        
        # 敵の移動
        self.enemy_y += 2
        if self.enemy_y > 120:  # 敵が画面を出たらリセット
            self.enemy_y = 0
            self.enemy_x = random.randint(0, 160)
            self.score += 1  # スコアを加算

        # 衝突判定
        if (self.player_x < self.enemy_x + 8 and
            self.player_x + 8 > self.enemy_x and
            self.player_y < self.enemy_y + 8 and
            self.player_y + 8 > self.enemy_y):
            pyxel.quit()  # 衝突したらゲームオーバー

    def draw(self):
        pyxel.cls(0)  # 画面クリア
        pyxel.rect(self.player_x, self.player_y, 8, 8, 9)  # プレイヤーを描画
        pyxel.rect(self.enemy_x, self.enemy_y, 8, 8, 8)  # 敵を描画
        pyxel.text(5, 5, f"Score: {self.score}", 7)  # スコア表示

SimpleActionGame()
```
![スクリーンショット 2024-12-22 16.02.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/6ecc2104-ca71-8447-e789-964a3d170511.png)

![スクリーンショット 2024-12-22 16.03.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/4dbd1886-6d59-d9c3-9961-7eb19a43a13a.png)

黄色が自機で、赤が敵。赤が上から降ってくるので黄色は避けるだけ。
動いたが、単純すぎる。

![スクリーンショット 2024-12-22 16.04.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/5fe8c164-1513-b45d-e064-54ab2cbfedee.png)
![スクリーンショット 2024-12-22 16.04.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/486061ed-6e2f-694a-76a6-0d4f7e5c5c20.png)

```game2.py
import pyxel
import random

class ShootingGame:
    def __init__(self):
        pyxel.init(160, 120, title="Shooting Game")
        self.player_x = 75
        self.player_y = 100
        self.bullets = []
        self.enemies = [{"x": random.randint(0, 152), "y": random.randint(-40, 0)} for _ in range(5)]
        self.score = 0
        pyxel.run(self.update, self.draw)

    def update(self):
        # プレイヤーの操作
        if pyxel.btn(pyxel.KEY_LEFT):
            self.player_x = max(self.player_x - 2, 0)
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.player_x = min(self.player_x + 2, 160 - 8)
        if pyxel.btnp(pyxel.KEY_SPACE):  # スペースキーで弾を発射
            self.bullets.append({"x": self.player_x + 3, "y": self.player_y})

        # 弾の移動
        for bullet in self.bullets:
            bullet["y"] -= 4
        self.bullets = [bullet for bullet in self.bullets if bullet["y"] > 0]

        # 敵の移動
        for enemy in self.enemies:
            enemy["y"] += 1
            if enemy["y"] > 120:  # 敵が画面外に出たらリセット
                enemy["y"] = random.randint(-40, 0)
                enemy["x"] = random.randint(0, 152)

        # 衝突判定（弾と敵）
        new_enemies = []
        for enemy in self.enemies:
            hit = False
            for bullet in self.bullets:
                if (bullet["x"] < enemy["x"] + 8 and
                    bullet["x"] + 2 > enemy["x"] and
                    bullet["y"] < enemy["y"] + 8 and
                    bullet["y"] + 2 > enemy["y"]):
                    hit = True
                    self.score += 1
            if not hit:
                new_enemies.append(enemy)
        self.enemies = new_enemies

    def draw(self):
        pyxel.cls(0)  # 画面クリア
        pyxel.rect(self.player_x, self.player_y, 8, 8, 9)  # プレイヤーを描画
        for bullet in self.bullets:
            pyxel.rect(bullet["x"], bullet["y"], 2, 4, 10)  # 弾を描画
        for enemy in self.enemies:
            pyxel.rect(enemy["x"], enemy["y"], 8, 8, 8)  # 敵を描画
        pyxel.text(5, 5, f"Score: {self.score}", 7)  # スコア表示

ShootingGame()
```
![スクリーンショット 2024-12-22 16.06.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/45ddd7f4-9665-45c5-bd76-79750cb0d7b4.png)

実行してみる

![スクリーンショット 2024-12-22 16.07.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/21d52723-90dc-3410-e1d3-91d38940bac7.png)

弾が打てるようになった！たくさん同時に敵も出てくる。

![スクリーンショット 2024-12-22 16.08.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/f040b357-b56d-70cb-cbe4-98a73546c718.png)

あれ？５機倒すと、敵が全く出てこなくなってしまう。

![スクリーンショット 2024-12-22 16.11.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/01cae045-7735-c287-e56e-5ec24d2620bd.png)
![スクリーンショット 2024-12-22 16.11.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/58683b02-e5fc-b562-6444-1f7b6fcc19db.png)

```game3.py
import pyxel
import random

class ShootingGame:
    def __init__(self):
        pyxel.init(160, 120, title="Shooting Game")
        self.player_x = 75
        self.player_y = 100
        self.bullets = []
        self.enemies = [{"x": random.randint(0, 152), "y": random.randint(-40, 0)} for _ in range(5)]
        self.score = 0
        pyxel.run(self.update, self.draw)

    def update(self):
        # プレイヤーの操作
        if pyxel.btn(pyxel.KEY_LEFT):
            self.player_x = max(self.player_x - 2, 0)
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.player_x = min(self.player_x + 2, 160 - 8)
        if pyxel.btnp(pyxel.KEY_SPACE):  # スペースキーで弾を発射
            self.bullets.append({"x": self.player_x + 3, "y": self.player_y})

        # 弾の移動
        for bullet in self.bullets:
            bullet["y"] -= 4
        self.bullets = [bullet for bullet in self.bullets if bullet["y"] > 0]

        # 敵の移動と再出現
        for enemy in self.enemies:
            enemy["y"] += 1
            if enemy["y"] > 120:  # 敵が画面外に出たらリセット
                enemy["y"] = random.randint(-40, 0)
                enemy["x"] = random.randint(0, 152)

        # 衝突判定（弾と敵）
        for enemy in self.enemies:
            for bullet in self.bullets:
                if (bullet["x"] < enemy["x"] + 8 and
                    bullet["x"] + 2 > enemy["x"] and
                    bullet["y"] < enemy["y"] + 8 and
                    bullet["y"] + 2 > enemy["y"]):
                    self.enemies.remove(enemy)  # 敵を削除
                    self.bullets.remove(bullet)  # 弾を削除
                    self.score += 1
                    # 新しい敵を生成
                    self.enemies.append({"x": random.randint(0, 152), "y": random.randint(-40, 0)})
                    break  # 衝突後にループを終了

    def draw(self):
        pyxel.cls(0)  # 画面クリア
        pyxel.rect(self.player_x, self.player_y, 8, 8, 9)  # プレイヤーを描画
        for bullet in self.bullets:
            pyxel.rect(bullet["x"], bullet["y"], 2, 4, 10)  # 弾を描画
        for enemy in self.enemies:
            pyxel.rect(enemy["x"], enemy["y"], 8, 8, 8)  # 敵を描画
        pyxel.text(5, 5, f"Score: {self.score}", 7)  # スコア表示

ShootingGame()
```
![スクリーンショット 2024-12-22 16.12.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/adb7b6ee-99c3-49c1-858b-d2842c0cfd18.png)

実行してみる
![スクリーンショット 2024-12-22 16.15.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/3887f279-91e1-dcf5-3b0b-af2a58b43cac.png)

Score 5以降も動くようになった。
だがしかし、敵とぶつかってもゲームオーパーにならず、無敵だ。

![スクリーンショット 2024-12-22 16.17.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/1bdc1baf-edbd-7493-16b9-3930bc1548d0.png)
![スクリーンショット 2024-12-22 16.17.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/a1242ba4-12d7-5950-82b1-e59967d902ba.png)

```game4.py
import pyxel
import random

class ShootingGame:
    def __init__(self):
        pyxel.init(160, 120, title="Shooting Game")
        self.player_x = 75
        self.player_y = 100
        self.bullets = []
        self.enemies = [{"x": random.randint(0, 152), "y": random.randint(-40, 0)} for _ in range(5)]
        self.score = 0
        self.game_over = False
        pyxel.run(self.update, self.draw)

    def update(self):
        if self.game_over:
            return  # ゲームオーバー時は更新を停止

        # プレイヤーの操作
        if pyxel.btn(pyxel.KEY_LEFT):
            self.player_x = max(self.player_x - 2, 0)
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.player_x = min(self.player_x + 2, 160 - 8)
        if pyxel.btnp(pyxel.KEY_SPACE):  # スペースキーで弾を発射
            self.bullets.append({"x": self.player_x + 3, "y": self.player_y})

        # 弾の移動
        for bullet in self.bullets:
            bullet["y"] -= 4
        self.bullets = [bullet for bullet in self.bullets if bullet["y"] > 0]

        # 敵の移動と再出現
        for enemy in self.enemies:
            enemy["y"] += 1
            if enemy["y"] > 120:  # 敵が画面外に出たらリセット
                enemy["y"] = random.randint(-40, 0)
                enemy["x"] = random.randint(0, 152)

        # 衝突判定（弾と敵）
        for enemy in self.enemies:
            for bullet in self.bullets:
                if (bullet["x"] < enemy["x"] + 8 and
                    bullet["x"] + 2 > enemy["x"] and
                    bullet["y"] < enemy["y"] + 8 and
                    bullet["y"] + 2 > enemy["y"]):
                    self.enemies.remove(enemy)  # 敵を削除
                    self.bullets.remove(bullet)  # 弾を削除
                    self.score += 1
                    # 新しい敵を生成
                    self.enemies.append({"x": random.randint(0, 152), "y": random.randint(-40, 0)})
                    break  # 衝突後にループを終了

        # 衝突判定（敵とプレイヤー）
        for enemy in self.enemies:
            if (self.player_x < enemy["x"] + 8 and
                self.player_x + 8 > enemy["x"] and
                self.player_y < enemy["y"] + 8 and
                self.player_y + 8 > enemy["y"]):
                self.game_over = True  # 衝突でゲームオーバー

    def draw(self):
        pyxel.cls(0)  # 画面クリア

        if self.game_over:
            pyxel.text(60, 50, "GAME OVER", pyxel.frame_count % 16)  # 点滅する文字
            pyxel.text(45, 70, f"Score: {self.score}", 7)
            return

        pyxel.rect(self.player_x, self.player_y, 8, 8, 9)  # プレイヤーを描画
        for bullet in self.bullets:
            pyxel.rect(bullet["x"], bullet["y"], 2, 4, 10)  # 弾を描画
        for enemy in self.enemies:
            pyxel.rect(enemy["x"], enemy["y"], 8, 8, 8)  # 敵を描画
        pyxel.text(5, 5, f"Score: {self.score}", 7)  # スコア表示

ShootingGame()
```

![スクリーンショット 2024-12-22 16.18.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/e58dd644-efa2-b838-771d-f30f48138eb3.png)

ほんとに？試してみる

![スクリーンショット 2024-12-22 16.19.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/c063e189-2445-53b3-6ffb-a6b9e9b85038.png)

ぶつかってみたところ、ゲームオーバー画面になった。
ただ、ここでキーを押しても何も起きず、この画面のままだ。

![スクリーンショット 2024-12-22 16.20.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/3f041c4a-411f-6995-441b-07bb173baf03.png)
![スクリーンショット 2024-12-22 16.20.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/6a25e102-152b-6687-58fc-b28ba5d2cb25.png)

```game5.py
import pyxel
import random

class ShootingGame:
    def __init__(self):
        pyxel.init(160, 120, title="Shooting Game")
        self.reset_game()
        pyxel.run(self.update, self.draw)

    def reset_game(self):
        """ゲームの状態をリセット"""
        self.player_x = 75
        self.player_y = 100
        self.bullets = []
        self.enemies = [{"x": random.randint(0, 152), "y": random.randint(-40, 0)} for _ in range(5)]
        self.score = 0
        self.game_over = False

    def update(self):
        if self.game_over:
            # ゲームオーバー時にキー入力でリスタート
            if pyxel.btnp(pyxel.KEY_SPACE):
                self.reset_game()
            return

        # プレイヤーの操作
        if pyxel.btn(pyxel.KEY_LEFT):
            self.player_x = max(self.player_x - 2, 0)
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.player_x = min(self.player_x + 2, 160 - 8)
        if pyxel.btnp(pyxel.KEY_SPACE):  # スペースキーで弾を発射
            self.bullets.append({"x": self.player_x + 3, "y": self.player_y})

        # 弾の移動
        for bullet in self.bullets:
            bullet["y"] -= 4
        self.bullets = [bullet for bullet in self.bullets if bullet["y"] > 0]

        # 敵の移動と再出現
        for enemy in self.enemies:
            enemy["y"] += 1
            if enemy["y"] > 120:  # 敵が画面外に出たらリセット
                enemy["y"] = random.randint(-40, 0)
                enemy["x"] = random.randint(0, 152)

        # 衝突判定（弾と敵）
        for enemy in self.enemies:
            for bullet in self.bullets:
                if (bullet["x"] < enemy["x"] + 8 and
                    bullet["x"] + 2 > enemy["x"] and
                    bullet["y"] < enemy["y"] + 8 and
                    bullet["y"] + 2 > enemy["y"]):
                    self.enemies.remove(enemy)  # 敵を削除
                    self.bullets.remove(bullet)  # 弾を削除
                    self.score += 1
                    # 新しい敵を生成
                    self.enemies.append({"x": random.randint(0, 152), "y": random.randint(-40, 0)})
                    break  # 衝突後にループを終了

        # 衝突判定（敵とプレイヤー）
        for enemy in self.enemies:
            if (self.player_x < enemy["x"] + 8 and
                self.player_x + 8 > enemy["x"] and
                self.player_y < enemy["y"] + 8 and
                self.player_y + 8 > enemy["y"]):
                self.game_over = True  # 衝突でゲームオーバー

    def draw(self):
        pyxel.cls(0)  # 画面クリア

        if self.game_over:
            pyxel.text(60, 50, "GAME OVER", pyxel.frame_count % 16)  # 点滅する文字
            pyxel.text(40, 70, "Press SPACE to Restart", 7)
            pyxel.text(60, 90, f"Score: {self.score}", 7)
            return

        pyxel.rect(self.player_x, self.player_y, 8, 8, 9)  # プレイヤーを描画
        for bullet in self.bullets:
            pyxel.rect(bullet["x"], bullet["y"], 2, 4, 10)  # 弾を描画
        for enemy in self.enemies:
            pyxel.rect(enemy["x"], enemy["y"], 8, 8, 8)  # 敵を描画
        pyxel.text(5, 5, f"Score: {self.score}", 7)  # スコア表示

ShootingGame()
```
![スクリーンショット 2024-12-22 16.21.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/cc441cf9-a420-fa3d-733d-26a09d2d6196.png)

本当か？実行してみよう。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Pyxelの実験 <a href="https://t.co/3BM4oNdRAS">pic.twitter.com/3BM4oNdRAS</a></p>&mdash; toss (@MakeTOSS) <a href="https://twitter.com/MakeTOSS/status/1870733961024184774?ref_src=twsrc%5Etfw">December 22, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

OKバッチリ！
ゲームオーバーの画面でスペースキーなど押せば再度ゲームも開始するようになった。

# まとめ

ChatGPTはPyxelを知っていて、インストール方法から、プログラムの生成まで可能。
指示として適切な内容を与えれば、１行もコードを書かずにこの程度のものは作れた。

以上、「pyxel」に今来た私が試したこと（ChatGPT先生と共に）でした。

最後までご覧いただきましてありがとうございました。