ChatGPTでお手軽ゲーム作成ハンズオン(python)

# はじめに
こんにちは！ゲーム大好きエンジニアです！

ゲームをやるのが好きで、いつか簡単なものでも作れないかな〜と思いつつなかなか足が踏み出せなかったのですが、ChatGPTの力を借りたらできるのでは！？と思いつき試しに挑戦してみました。
とても簡単にお手軽ゲームを作成することができたので、ハンズオン形式で残そうと思います！ハンズオンに興味がない方は**ゲームのルールを伝えてコードを考えてもらう**の章からご覧ください。

# 開発環境準備

## ゲーム作りに最適な開発言語を聞いてみる

まず、ゲームに最適な開発言語をChatGPTに聞いてみましょう。

https://chat.openai.com/auth/login

ログインしたのち、以下プロンプトを入力します。

```
簡単なゲームを作るおすすめの言語を教えてください
```

結果
```
ゲームを作成するためのおすすめのプログラミング言語は、いくつかあります。以下は特に初心者にとって扱いやすく、豊富なリソースがある人気のある言語です：

1. Python（パイソン）: Pythonはシンプルで読みやすい構文を持つプログラミング言語で、多くの初心者にとって最適な選択です。PygameというPythonのゲームライブラリもあり、2Dゲームを作成するのに適しています。

2. Unity (C#): Unityはゲーム開発のための非常に人気のあるゲームエンジンです。C#を使ってスクリプトを書くことができ、2Dと3Dの両方のゲームを作成できます。豊富なドキュメントとコミュニティがあります。

3. JavaScript: JavaScriptはウェブブラウザ上で動作するゲームを作成するのに最適です。HTML5とCanvasを使って2Dゲームを作成することができます。PhaserやPixi.jsなどのライブラリがJavaScriptゲーム開発をサポートします。

4. Godot (GDScript): Godotは無料でオープンソースのゲームエンジンで、GDScriptという独自のスクリプト言語を使います。Godotは2Dと3Dの両方のゲームを作成することができます。

5. Scratch: プログラミング初心者向けに設計されたScratchはビジュアルプログラミング言語です。簡単なゲームを作成するのに適しており、学習やプロトタイピングにも便利です。

これらの言語は、ゲーム開発の基本を学ぶには非常に適しています。言語の選択は、個人の目標とプロジェクトの要件によって異なりますので、プロジェクトのスコープや目標に合った言語を選ぶと良いでしょう。
```

今回はPythonを使用して開発してみます！

# 開発環境構築
## pythonのインストール
筆者はmacを使用しているため、macの環境構築手順を紹介します。
python3系統の環境が手元にある方はこの章は飛ばし、**pygameのインストール**から実施をお願いします。

まずpyenvをインストールします。(homebrewをインストールしていない方はこちらのインストールも実施してください。）
ターミナルで以下コマンドを実行してください。

```
$ brew install pyenv
```

次に、pyenvのパスを通すためにbash_profileを編集・保存します。
ターミナルで以下コマンドを実行してください。

```
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
```

3系統のpythonをインストールします。筆者は3.11.4をインストールしました。

```
$ pyenv install 3.11.4
```

ここで、ゲームのコードを保存するフォルダを作成し、その中にターミナル上で移動してください。
移動した先で以下コマンドを入力し、pythonのバージョンの反映と確認を行います。
```
$ pyenv local 3.11.4
$ python --version
Python 3.11.4
```

## pygameのインストール
次に、pygameライブラリのインストールを行います
```
$ pip install pygame
```

これで開発環境構築は終了です！

# ゲームのルールを伝えてコードを考えてもらう

作ってほしいゲームを言語化し、ChatGPTに伝えてみます。今回は以下のようなスクリプトを指定してみました。

```
Pythonのpygameライブラリを使用してゲームを作りたいです。
ゲームのルールは以下の通りです。
・プレイヤーは「●」で画面上に表示されています
・プレイヤーは画面最下部を矢印キーで左右に移動することができます
・画面最上部から、ランダムにアイテムがゆっくりと落ちてきます
・一度に画面上に表示されるアイテム数は最大5つです
・アイテムは「★」で画面上に表示されています
・アイテムは画面最下部に到達すると消えてしまいます
・プレイヤーとアイテムの座標が一致した時、プレイヤーのスコアが100加算されます
・1ゲームのプレイ時間は30秒で、スコアの合計得点を最後に算出してください
```

結果
```
面白そうなゲームのアイデアですね！Pythonのpygameライブラリを使用してこれを実現するための基本的なコードを以下に示します。ただし、完全なゲームを作成するためにはさらなる機能と改善が必要かもしれませんが、ここではルールに基づいた基本的な実装を提供します。

まず、pygameをインストールしてから以下のコードを実行してください。
...
pythonのコード出力
...
このコードは、アイテムキャッチゲームの基本的な動作を実現しています。プレイヤーのスコアと残り時間が画面上に表示され、30秒のゲームプレイが行われます。

これは初歩的な例ですので、より洗練されたゲームにするには、例えば以下のような改善点があります。

1. グラフィックや音楽を追加してゲームを演出する
2. ゲームオーバーの表示と再スタートの機能を追加する
3. 難易度を調整するためのアイテムの速度や出現頻度の調整
4. プレイヤーの移動制限（画面外に出ないようにするなど）を追加する
5. ゲームのステージやレベルを追加する
6. ハイスコアの記録と表示を追加する

これらの要素を追加して、より充実したゲーム体験を提供することができます。
```

実際に出力されたコードを作成したフォルダのmain.pyにコピペします
```main.py
import pygame
import random
import sys

pygame.init()

# 画面のサイズと初期化
screen_width = 400
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("アイテムキャッチゲーム")

# 色の定義
WHITE = (255, 255, 255)
RED = (255, 0, 0)

# プレイヤーの初期設定
player_size = 50
player_pos = [screen_width // 2, screen_height - player_size * 2]

# アイテムの初期設定
item_size = 25
item_pos = [random.randint(0, screen_width - item_size), 0]
items = [item_pos]

# ゲームの設定
score = 0
clock = pygame.time.Clock()
game_font = pygame.font.Font(None, 40)
game_duration = 30  # ゲームのプレイ時間（秒）
game_timer = pygame.time.get_ticks() + game_duration * 1000

# イベントループ
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        # プレイヤーの操作
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                player_pos[0] -= player_size
            elif event.key == pygame.K_RIGHT:
                player_pos[0] += player_size

    # 背景をクリア
    screen.fill(WHITE)

    # プレイヤーを描画
    pygame.draw.rect(screen, RED, (player_pos[0], player_pos[1], player_size, player_size))

    # アイテムを描画
    if len(items) < 5 and random.randint(0, 50) == 0:
        items.append([random.randint(0, screen_width - item_size), 0])

    for item_pos in items:
        pygame.draw.rect(screen, RED, (item_pos[0], item_pos[1], item_size, item_size))

    # アイテムの移動とプレイヤーとの当たり判定
    for idx, item_pos in enumerate(items):
        if item_pos[1] >= 0 and item_pos[1] < screen_height:
            item_pos[1] += 5
        else:
            items.pop(idx)

        # 当たり判定
        if item_pos[1] >= player_pos[1] and item_pos[1] <= player_pos[1] + player_size:
            if item_pos[0] >= player_pos[0] and item_pos[0] <= player_pos[0] + player_size:
                score += 100
                items.pop(idx)

    # スコアを表示
    score_text = f"スコア: {score}"
    text_render = game_font.render(score_text, True, RED)
    screen.blit(text_render, (10, 10))

    # 残り時間を表示
    remaining_time = max(0, (game_timer - pygame.time.get_ticks()) // 1000)
    time_text = f"残り時間: {remaining_time} 秒"
    time_render = game_font.render(time_text, True, RED)
    screen.blit(time_render, (screen_width - 200, 10))

    # ゲーム終了条件
    if pygame.time.get_ticks() >= game_timer:
        pygame.quit()
        sys.exit()

    # 画面を更新
    pygame.display.update()
    clock.tick(60)
```

早速実行してみましょう！ターミナルで以下コマンドを実行して起動してみます。
```
$ python3 main.py
```

実行結果はこんな感じになりました！上の表示が文字化けしている以外はちゃんと動いています！！すごい！！！

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476797/8f2527dd-a92a-e6e4-a7aa-16e01238766c.gif">

# 追加機能を実装してもらう

動き自体はできているのですが、今の実装では、残り時間が0秒になった瞬間にゲームが自動で落ちてしまい、自分のスコアを確認する時間がありません。

せっかくなので、文字化けを直すついでにChatGPTに提案してもらっていた**2. ゲームオーバーの表示と再スタートの機能を追加する**を追加実装してもらおうと思います。

スクリプト
```
ゲーム終了時に以下の条件の画面を表示するように修正してください
・「Congratulations!」という文字を表示する
・プレイヤーが獲得したスコアを表示する
・「再スタート」ボタンが表示されており、押すとゲームをもう一度プレイすることができる
```

結果
```
ゲーム終了時に「Congratulations!」とスコアを表示し、再スタートボタンを追加するために、以下の修正を行います。この修正では、ゲームオーバー画面を専用の関数として作成し、再スタートボタンを追加します。
...
pythonのコード出力
...
これで、ゲーム終了時に「Congratulations!」とスコア、そして「再スタート」ボタンが表示されるようになります。プレイヤーが「再スタート」ボタンを押すと、ゲームがもう一度プレイされます。
```

結果に基づいて修正したコード
```main.py
import pygame
import random
import sys

pygame.init()

# 画面のサイズと初期化
screen_width = 400
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("アイテムキャッチゲーム")

# 色の定義
WHITE = (255, 255, 255)
RED = (255, 0, 0)

# プレイヤーの初期設定
player_size = 50
player_pos = [screen_width // 2, screen_height - player_size * 2]

# アイテムの初期設定
item_size = 25
item_pos = [random.randint(0, screen_width - item_size), 0]
items = [item_pos]

# ゲームの設定
score = 0
clock = pygame.time.Clock()
game_font = pygame.font.Font(None, 40)
game_duration = 30  # ゲームのプレイ時間（秒）
game_timer = pygame.time.get_ticks() + game_duration * 1000

def show_game_over_screen():
    screen.fill(WHITE)

    # 「Congratulations!」の表示
    game_over_font = pygame.font.Font(None, 60)
    game_over_text = game_over_font.render("Congratulations!", True, RED)
    screen.blit(game_over_text, (screen_width // 2 - 170, screen_height // 2 - 50))

    # スコアの表示
    score_text = f"score: {score}"
    score_render = game_font.render(score_text, True, RED)
    screen.blit(score_render, (screen_width // 2 - 70, screen_height // 2 + 10))

    # 「再スタート」ボタンの表示
    restart_font = pygame.font.Font(None, 40)
    restart_text = restart_font.render("restart", True, RED)
    restart_rect = restart_text.get_rect(center=(screen_width // 2, screen_height // 2 + 70))
    pygame.draw.rect(screen, WHITE, restart_rect)
    screen.blit(restart_text, restart_rect)

    pygame.display.update()

    # ボタンが押されるまで待機
    waiting = True
    while waiting:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_x, mouse_y = pygame.mouse.get_pos()
                if restart_rect.collidepoint(mouse_x, mouse_y):
                    return

# イベントループ
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        # プレイヤーの操作
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                player_pos[0] -= player_size
            elif event.key == pygame.K_RIGHT:
                player_pos[0] += player_size

    # 背景をクリア
    screen.fill(WHITE)

    # プレイヤーを描画
    pygame.draw.rect(screen, RED, (player_pos[0], player_pos[1], player_size, player_size))

    # アイテムを描画
    if len(items) < 5 and random.randint(0, 50) == 0:
        items.append([random.randint(0, screen_width - item_size), 0])

    for item_pos in items:
        pygame.draw.rect(screen, RED, (item_pos[0], item_pos[1], item_size, item_size))

    # アイテムの移動とプレイヤーとの当たり判定
    for idx, item_pos in enumerate(items):
        if item_pos[1] >= 0 and item_pos[1] < screen_height:
            item_pos[1] += 5
        else:
            items.pop(idx)

        # 当たり判定
        if item_pos[1] >= player_pos[1] and item_pos[1] <= player_pos[1] + player_size:
            if item_pos[0] >= player_pos[0] and item_pos[0] <= player_pos[0] + player_size:
                score += 100
                items.pop(idx)

    # スコアを表示
    score_text = f"score: {score}"
    text_render = game_font.render(score_text, True, RED)
    screen.blit(text_render, (10, 10))

    # 残り時間を表示
    remaining_time = max(0, (game_timer - pygame.time.get_ticks()) // 1000)
    time_text = f"{remaining_time} seconds left"
    time_render = game_font.render(time_text, True, RED)
    screen.blit(time_render, (screen_width - 200, 10))

    # ゲーム終了条件
    if pygame.time.get_ticks() >= game_timer:
        show_game_over_screen()
        # ゲームをリスタートするためにスコアとアイテムを初期化
        score = 0
        items = [item_pos]
        game_timer = pygame.time.get_ticks() + game_duration * 1000

    # 画面を更新
    pygame.display.update()
    clock.tick(60)
```

ちゃんと実装されているか再び確認してみましょう。ターミナルから起動してみます。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476797/55016dac-8f01-53bf-ed48-c8885a168fa2.png">
<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476797/4fc41c50-1fd4-53b7-9b3c-aaef532fcab8.png">

文字化けが直り、ゲーム終了後に結果が確認できるようになりました！
restartボタンを押すとちゃんともう一度プレイができるようになっていました...！！さすがです。

# やってみた感想
pythonは知っていてもゲームは作ったことないし...と最初の一歩を踏み出しきれていなかった今まででしたが、簡単に土台が作れるので、その先のどう工夫していくか、どうしていきたいかの方に時間が割けるのがありがたいなと感じました。
一方で、作成したいプロダクトを1発で伝えて実装してもらうのは難しく、何度かやりとりをする中でどれだけ言語化をうまく行うかが重要になってくると感じました。

# まとめ
夢だったゲーム制作がお手軽なものであればこんなに簡単に作れてしまうのかと感動しました...！
もっと複雑なゲームを作成したり、違う言語で挑戦してみたり、今回作成した「アイテムキャッチゲーム」をよりゲームっぽくしてみたりしたいな〜と色々考えられるのもいい点ですね。

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