👻

【Pygame】プログラミング初心者がChat GPT を活用してサバイバルゲーム開発してみた話

2024/11/29に公開

1. はじめに

はじめまして、Chat GPT を活用してプログラミング学習中のAmy(エイミー)です。
具体的にはPythonでゲーム開発をしています。

少しずつではあるものの自力でコードを書けるかけるようになってきたため、今回は少し難易度を上げて、サバイバルゲームの作成に挑戦しました!
先に完成品をお見せすると、こんな感じです!どうですか!?オリジナリティ溢れるゲームになったかなと思っています✨
https://www.youtube.com/watch?v=zPFta-tmf_0

本記事ではpygameライブラリを使ってPythonで簡単にサバイバルゲームを作成した方法と実際のコードを紹介します。

また、流れとしてはChat GPT を活用してベースとなるソースコードを作成し、そこへ欲しい追加機能を実装しました。
ビフォーアフター動画はこちらです。
https://youtube.com/shorts/c8g49hoz2cA

Chat GPTを活用すれば、初心者でも本格的なゲームを作ることができます。
Python学習中の方の参考になれば幸いです。それでは行ってみましょう💨

・Pythonでプログラミングを始めたばかりの人
・自作ゲーム開発に興味がある人
・学んだ知識を使って、簡単なプログラムを書いてみたい人
・実際に何を作ればよいか迷っている方

2. 必要な準備

1. 実行環境

使ったものは以下のとおりです。

  • Windows 10 Pro
  • Python 3.13.0
  • ChatGPT
  • Pygame※
  • Visual Studio Code
    ※PygameはPythonでのゲーム作成に広く使われているライブラリです。このライブラリを使うと、グラフィック、サウンド、イベント処理が簡単にできるのでゲーム制作にはおすすめです。

2. 事前準備

PygameライブラリをPCにインストールします。
(Pythonのインストール方法については省略しています。)

一例として、コマンドでインストールする方法をご紹介します。
<手順>
コマンドプロンプトで以下のコマンドを実行しインストールします。

pip install pygame

実行後、Successfully installed pygame -version ~と表示されればOKです。

3. ゲーム設計

今回は プレイヤーである勇者が敵であるお化けから30秒間逃げて生き延びるゲーム を作成しました。

ルールは簡単で敵から逃げ切るとゲームクリアで、HPが0になったらゲームオーバーです。
また、敵に接触するとダメージを受けます。
さらに、アイテムもランダムで出現し、HP回復、スピード、敵を一時停止などの効果があります。

主なゲームフローは以下のとおりです。

  1. プレイヤー(勇者)は矢印キーで移動できます。
  2. アイテムはランダムな位置に配置されます。
  • 食べ物に触れると体力が回復し、ランダムな位置に再配置されます。
  • 宝箱に触れると敵が一時的にフリーズます。
  • ポーションに触れるとプレイヤーの移動速度が一時的に早くなります。
  1. 敵(お化け)はプレイヤーを追尾してきて、接触するとプレイヤーの体力が減少します。
  2. 体力がゼロになるとゲームオーバーです。

4. 開発

1. 基本のソースコードを準備します。

はじめにChatGPTでサバイバルゲームの基本的なコードを生成してもらいます。

以下のコードが生成されたコードです。

基本コード
sample.py
import pygame
import random
import sys

# 初期化
pygame.init()
WIDTH, HEIGHT = 800, 600
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("サバイバルゲーム")
FONT = pygame.font.Font(None, 36)

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

# プレイヤー設定
player_pos = [WIDTH // 2, HEIGHT // 2]
player_size = 30
player_color = BLUE
health = 100

# 食べ物の設定
food_size = 20
food_pos = [random.randint(0, WIDTH - food_size), random.randint(0, HEIGHT - food_size)]
food_color = GREEN

# 敵の設定
enemy_size = 30
enemy_pos = [random.randint(0, WIDTH - enemy_size), random.randint(0, HEIGHT - enemy_size)]
enemy_color = RED
enemy_speed = 5

clock = pygame.time.Clock()

# ゲームループ
running = True
while running:
    SCREEN.fill(WHITE)

    # イベント処理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # プレイヤーの移動
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_pos[0] > 0:
        player_pos[0] -= 5
    if keys[pygame.K_RIGHT] and player_pos[0] < WIDTH - player_size:
        player_pos[0] += 5
    if keys[pygame.K_UP] and player_pos[1] > 0:
        player_pos[1] -= 5
    if keys[pygame.K_DOWN] and player_pos[1] < HEIGHT - player_size:
        player_pos[1] += 5

    # 食べ物の取得
    player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
    food_rect = pygame.Rect(food_pos[0], food_pos[1], food_size, food_size)
    if player_rect.colliderect(food_rect):
        health += 20  # 食べ物で体力回復
        food_pos = [random.randint(0, WIDTH - food_size), random.randint(0, HEIGHT - food_size)]

    # 敵の追尾
    if enemy_pos[0] < player_pos[0]:
        enemy_pos[0] += enemy_speed
    if enemy_pos[0] > player_pos[0]:
        enemy_pos[0] -= enemy_speed
    if enemy_pos[1] < player_pos[1]:
        enemy_pos[1] += enemy_speed
    if enemy_pos[1] > player_pos[1]:
        enemy_pos[1] -= enemy_speed

    # 敵との衝突判定
    enemy_rect = pygame.Rect(enemy_pos[0], enemy_pos[1], enemy_size, enemy_size)
    if player_rect.colliderect(enemy_rect):
        health -= 1  # 敵と衝突すると体力減少

    # ゲームオーバー判定
    if health <= 0:
        running = False
        print("ゲームオーバー!")

    # 画面に描画
    pygame.draw.rect(SCREEN, player_color, player_rect)
    pygame.draw.rect(SCREEN, food_color, food_rect)
    pygame.draw.rect(SCREEN, enemy_color, enemy_rect)

    # 体力表示
    health_text = FONT.render(f"体力: {health}", True, (0, 0, 0))
    SCREEN.blit(health_text, (10, 10))

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

pygame.quit()
sys.exit()



▼実行結果

青色がプレイヤー、赤色が敵、緑色が食べ物を表しているようです。
敵はプレイヤーを追いかけ、当たると体力が減少する仕様です。

また、左上は体力の数値です。
おそらく日本語がうまく表示されていないため□□になっているようです。。。

▼プレイ動画
https://youtu.be/xFJb21Y5M9c

やはり、ゲーム性を高めるには改善の余地がまだまだありそうです。

そこで、以下の7点を実装&修正することにしました。

・複数の敵をランダムに生成する。
・プレイヤーが30秒間逃げ切ったらゲームクリアにする
・画面上に残り時間、HP、アイテムの効果を表示する
・プレイヤー、敵、アイテム、背景に画像を設定する
・アイテムの追加
①フリーズアイテム: 一定時間敵の動きを止める。
②速度アップ: 一定時間プレイヤーの移動速度を上げる。
・効果音を出す。
・ゲームオーバー/ゲームクリア画面を表示する。

追加の機能もコードもChat GPTに聞きながら実装しました。
追加機能を基本コードに実装していきます。


2. 複数の敵をランダムに生成する。

<手順>

  1. 敵リストを作成します。
    各敵の位置と速度をリストで管理します。
# 敵
enemy_size = 30
enemies = [{"pos": [random.randint(0, WIDTH), random.randint(0, HEIGHT)], "speed": random.randint(2, 4)} for _ in range(5)]
enemy_color = RED
enemy_speed = 3
  1. 敵の移動処理を追加します。
    各敵がプレイヤーを追尾する動きを実装します。衝突判定コードも修正します。
# 敵の移動と追尾
for enemy in enemies:
    if enemy["pos"][0] < player_pos[0]:
        enemy["pos"][0] += enemy["speed"]
    if enemy["pos"][0] > player_pos[0]:
        enemy["pos"][0] -= enemy["speed"]
    if enemy["pos"][1] < player_pos[1]:
        enemy["pos"][1] += enemy["speed"]
    if enemy["pos"][1] > player_pos[1]:
        enemy["pos"][1] -= enemy["speed"]

# 敵との衝突判定
for enemy in enemies:
    enemy_rect = pygame.Rect(enemy["pos"][0], enemy["pos"][1], enemy_size, enemy_size)
    if player_rect.colliderect(enemy_rect):
        health -= 1  # 衝突時に体力減少
        if health <= 0:
            running = False
            print("ゲームオーバー!")

  1. 敵の描画で全ての敵を画面に表示します。
    # 敵の描画
    for enemy in enemies:
        pygame.draw.rect(SCREEN, RED, (enemy["pos"][0], enemy["pos"][1], enemy_size, enemy_size))

3. アイテムの追加

以下2つのアイテムを追加します。
①フリーズアイテム: 一定時間敵の動きを止める。
②速度アップ: 一定時間プレイヤーの移動速度を上げる。

<手順>

  1. アイテムリストを作成します。
    プレイヤーがアイテムを取得したときの効果を定義。
# アイテム
item_size = 20
items = [{"pos": [random.randint(0, WIDTH), random.randint(0, HEIGHT)], "type": random.choice(["speed", "freeze"])} for _ in range(2)]
speed_boost_time = 0  # 移動速度アップの効果時間
  1. ゲームループ内でプレイヤーの移動コードを修正します。
    # プレイヤー移動
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_pos[0] > 0:
        player_pos[0] -= player_speed
    if keys[pygame.K_RIGHT] and player_pos[0] < WIDTH - player_size:
        player_pos[0] += player_speed
    if keys[pygame.K_UP] and player_pos[1] > 0:
        player_pos[1] -= player_speed
    if keys[pygame.K_DOWN] and player_pos[1] < HEIGHT - player_size:
        player_pos[1] += player_speed
  1. フリーズ効果がない場合のみ敵を動かすように敵の移動コードを修正します。
    # 敵の移動と追尾
    if freeze_time <= 0:  # フリーズ効果がない場合のみ敵を動かす
        for enemy in enemies:
            if enemy["pos"][0] < player_pos[0]:
#以下省略
  1. プレイヤーがアイテムを取得したときの効果に関するコードを追加します。
    ・スピードアップアイテム: speed_boost_time が0より大きい間、プレイヤーの移動速度をアップ。
    効果が切れたら速度を通常値に戻します。
    ・フリーズアイテム: freeze_time が0より大きい間、敵の移動を停止。
    効果が切れたら通常の敵の動作に戻します。
        # アイテム取得
        for item in items:
            item_rect = pygame.Rect(item["pos"][0], item["pos"][1], item_size, item_size)
            if player_rect.colliderect(item_rect):
                if item["type"] == "speed":
                    speed_boost_time = 200  # 移動速度アップを200フレーム持続
                    player_speed = 8
                elif item["type"] == "freeze":
                    freeze_time = 200  # フリーズ効果を200フレーム持続

                items.remove(item)  # アイテムを削除
                break

        # 効果時間の管理
        if speed_boost_time > 0:
            speed_boost_time -= 1
        else:
            player_speed = 5  # 通常速度に戻す

        if freeze_time > 0:
            freeze_time -= 1
  1. アイテムの描写コードを追加します。
    # アイテムの描画
    for item in items:
        color = CYAN if item["type"] == "freeze" else YELLOW
        pygame.draw.rect(SCREEN, color, (item["pos"][0], item["pos"][1], item_size, item_size))

4. プレイヤー、敵、アイテム、背景に画像を設定する

<手順>

  1. 画像pngファイルをVisual Studio Code上にアップロードします。
    使用する画像は以下のフリー素材を利用しました。

https://dot-illust.net/

背景はこちらのフリー素材を利用しました。
https://game-materials.com/

  1. 画像ファイルを読み込むコード、画像サイズを設定するコードを追加します。
#画像を読み込む
player_image_original = pygame.image.load('player.png')
enemy_image_original = pygame.image.load('enemy.png')
food_image_original = pygame.image.load('food.png')
speed_image_original = pygame.image.load('speed.png')
freeze_image_original = pygame.image.load('freeze.png')
background_image_original = pygame.image.load("background.png")
gameclear_image_original = pygame.image.load("gameclear.png")

# 画像のサイズを設定
player_image = pygame.transform.scale(player_image_original, (50, 50))
enemy_image = pygame.transform.scale(enemy_image_original, (50, 50))
food_image = pygame.transform.scale(food_image_original, (30, 30))
speed_image = pygame.transform.scale(speed_image_original, (30, 30))
freeze_image = pygame.transform.scale(freeze_image_original, (30, 30))
background_image = pygame.transform.scale(background_image_original, (WIDTH, HEIGHT))
gameclear_image = pygame.transform.scale(gameclear_image_original, (WIDTH, HEIGHT))
  1. ゲームループ内のscreen.fill(WHITE)を背景画像を描画する以下のコードに修正します。
# ゲームループ
while running:
    # 背景画像を描画
    SCREEN.blit(background_image, (0, 0))
  1. ゲームループ内に画像を描画する以下のコードに修正します。
# 敵の描画
        for enemy in enemies:
            SCREEN.blit(enemy_image, (enemy["pos"][0], enemy["pos"][1]))

        # アイテムの描画
        for item in items:
            if item["type"] == "speed":
                SCREEN.blit(speed_image, (item["pos"][0], item["pos"][1]))
            elif item["type"] == "freeze":
                SCREEN.blit(freeze_image, (item["pos"][0], item["pos"][1]))

        # プレイヤーの描画
        SCREEN.blit(player_image, player_rect)  

        # 食べ物の描画
        SCREEN.blit(food_image, food_rect)

5. ゲームオーバー/ゲームクリア画面を表示する

<手順>

  1. ゲームオーバー画面/ゲームクリア画面の表示関数を作成します。
#ゲームオーバー画面の表示関数
def game_over_screen():
    SCREEN.blit(background_image, (0, 0))
    font = pygame.font.Font(None, 72)
    text = font.render("GAME OVER", True, RED)
    SCREEN.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2 - 100))
    
#ゲームクリア画面の表示関数
def game_clear_screen():
    SCREEN.blit(gameclear_image, (0, 0))
    font = pygame.font.Font(None, 72)
    text = font.render("GAME CLEAR", True, WHITE)
    SCREEN.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2 - 100))
  1. ゲーム状態の変数を作成します。
# ゲーム状態
game_state = "playing"  # "playing", "game_over", "game_clear"
  1. ゲームループ内でgame_state 条件分岐するようにIf-else文に修正します。
    if health <= 0: の場合は game_state == "game_over" となり、ゲームオーバー画面の表示関数を実行します。

if elapsed_time >= time_limit: の場合はgame_state == "game_clear" となり、ゲームクリア画面の表示関数を実行します。

    if game_state == "playing":
        # 経過時間計算
        elapsed_time = pygame.time.get_ticks() - start_time
        remaining_time = max(0, (time_limit - elapsed_time) // 1000)  # 残り時間(秒)

        # プレイヤー移動
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and player_pos[0] > 0:
            player_pos[0] -= player_speed
        if keys[pygame.K_RIGHT] and player_pos[0] < WIDTH - player_size:
            player_pos[0] += player_speed
        if keys[pygame.K_UP] and player_pos[1] > 0:
            player_pos[1] -= player_speed
        if keys[pygame.K_DOWN] and player_pos[1] < HEIGHT - player_size:
            player_pos[1] += player_speed

        # 食べ物の取得
        player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
        food_rect = pygame.Rect(food_pos[0], food_pos[1], food_size, food_size)
        if player_rect.colliderect(food_rect):
            health += 20  # 食べ物で体力回復
            food_pos = [random.randint(0, WIDTH - food_size), random.randint(0, HEIGHT - food_size)]

        
        # 敵の移動と追尾
        if freeze_time <= 0:  # フリーズ効果がない場合のみ敵を動かす
            for enemy in enemies:
                if enemy["pos"][0] < player_pos[0]:
                    enemy["pos"][0] += enemy["speed"]
                if enemy["pos"][0] > player_pos[0]:
                    enemy["pos"][0] -= enemy["speed"]
                if enemy["pos"][1] < player_pos[1]:
                    enemy["pos"][1] += enemy["speed"]
                if enemy["pos"][1] > player_pos[1]:
                    enemy["pos"][1] -= enemy["speed"]

        # 敵との衝突判定
        for enemy in enemies:
            enemy_rect = pygame.Rect(enemy["pos"][0], enemy["pos"][1], enemy_size, enemy_size)
            if player_rect.colliderect(enemy_rect):
                health -= 1  # 衝突時に体力減少
                if health <= 0:
                    game_state = "game_over"

        # アイテム取得
        for item in items:
            item_rect = pygame.Rect(item["pos"][0], item["pos"][1], item_size, item_size)
            if player_rect.colliderect(item_rect):
                if item["type"] == "speed":
                    speed_boost_time = 200  # 移動速度アップを200フレーム持続
                    player_speed = 8
                elif item["type"] == "freeze":
                    freeze_time = 200  # フリーズ効果を200フレーム持続

                items.remove(item)  # アイテムを削除
                break

        # 効果時間の管理
        if speed_boost_time > 0:
            speed_boost_time -= 1
        else:
            player_speed = 5  # 通常速度に戻す

        if freeze_time > 0:
            freeze_time -= 1

        # 敵の描画
        for enemy in enemies:
            SCREEN.blit(enemy_image, (enemy["pos"][0], enemy["pos"][1]))

        # アイテムの描画
        for item in items:
            if item["type"] == "speed":
                SCREEN.blit(speed_image, (item["pos"][0], item["pos"][1]))
            elif item["type"] == "freeze":
                SCREEN.blit(freeze_image, (item["pos"][0], item["pos"][1]))

        # プレイヤーの描画
        SCREEN.blit(player_image, player_rect)  

        # 食べ物の描画
        SCREEN.blit(food_image, food_rect)

        # 体力表示
        health_text = FONT.render(f"体力: {health}", True, (0, 0, 0))
        SCREEN.blit(health_text, (10, 10))

        # クリア判定
        if elapsed_time >= time_limit:
            game_state = "game_clear"

    elif game_state == "game_over":
        # ゲームオーバー画面の表示
        game_over_screen()

    elif game_state == "game_clear":
        # ゲームクリア画面の表示
        game_clear_screen()

6. プレイヤーが30秒間逃げ切ったらゲームクリアにする

<手順>

  1. クリア条件変数を作成します。
# クリア条件(時間計測)
start_time = pygame.time.get_ticks()  # ゲーム開始時の時間
time_limit = 30000  # 30秒(ミリ秒)
  1. ゲームループ内で経過時間を計測するコードを追加します。
if game_state == "playing":
        # 経過時間計算
        elapsed_time = pygame.time.get_ticks() - start_time
        remaining_time = max(0, (time_limit - elapsed_time) // 1000)  # 残り時間(秒)
  1. ゲームループ内クリア判定をするコードを追加します。
# クリア判定
        if elapsed_time >= time_limit:
            game_state = "game_clear"

7. 効果音を出す

<手順>

  1. 効果音のmp3ファイルをVisual Studio Code上にアップロードし、wav形式にRenameします。

https://www.springin.org/sound-stock/

  1. 効果音ファイルを読み込むコードを追加します。
# 効果音
pygame.mixer.init()

eat_sound = pygame.mixer.Sound("eat.wav")
freeze_sound = pygame.mixer.Sound("freeze.wav")
speed_sound = pygame.mixer.Sound("speed.wav")
damage_sound = pygame.mixer.Sound("damage.wav")
gameclear_sound = pygame.mixer.Sound("gameclear.wav")
  1. ゲームループ内で効果音を再生するコードを追加します。
    回復時の効果音
        # 食べ物の取得
        player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
        food_rect = pygame.Rect(food_pos[0], food_pos[1], food_size, food_size)
        if player_rect.colliderect(food_rect):
            health += 20  # 食べ物で体力回復
            food_pos = [random.randint(0, WIDTH - food_size), random.randint(0, HEIGHT - food_size)]
            eat_sound.play()

ダメージ時の効果音

        # 敵との衝突判定
        for enemy in enemies:
            enemy_rect = pygame.Rect(enemy["pos"][0], enemy["pos"][1], enemy_size, enemy_size)
            if player_rect.colliderect(enemy_rect):
                health -= 1  # 衝突時に体力減少
                damage_sound.play()
                if health <= 0:
                    game_state = "game_over"

アイテム取得時の効果音

        # アイテム取得
        for item in items:
            item_rect = pygame.Rect(item["pos"][0], item["pos"][1], item_size, item_size)
            if player_rect.colliderect(item_rect):
                if item["type"] == "speed":
                    speed_boost_time = 200  # 移動速度アップを200フレーム持続
                    player_speed = 8
                    speed_sound.play()
                elif item["type"] == "freeze":
                    freeze_time = 200  # フリーズ効果を200フレーム持続
                    freeze_sound.play()

ゲームクリア時の効果音

        # クリア判定
        if elapsed_time >= time_limit:
            # ゲームクリア音を鳴らす
            gameclear_sound.play()
            game_state = "game_clear"

8. 画面上に残り時間、HP、アイテムの効果を表示する

最後の手順です。
<手順>

  1. テキストでそれぞれの値を表示するコードを追加します。
        # 体力の表示
        health_text = FONT.render(f"HP: {health}", True, WHITE)
        SCREEN.blit(health_text, (10, 10))

        #効果時間の表示
        if speed_boost_time > 0:
            speed_text = FONT.render("SPEED UP TIME!", True, RED)
            SCREEN.blit(speed_text, (10, 100))
        if freeze_time > 0:
            freeze_text = FONT.render("FREEZE!", True, BLUE)
            SCREEN.blit(freeze_text, (10, 70))

        #残り時間の表示
        timer_text = FONT.render(f"Time Left: {remaining_time} s", True, WHITE)
        SCREEN.blit(timer_text, (10, 40))

5. 完成!

上記を修正した最終的なコード全体はこんな感じです。

コード最終形態
game.py
import pygame
import random
import sys

# 初期化
pygame.init()
WIDTH, HEIGHT = 800, 600
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("サバイバルゲーム")
FONT = pygame.font.Font(None, 36)

# 色の定義
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
CYAN = (0, 255, 255)

#画像を読み込む
player_image_original = pygame.image.load('player.png')
enemy_image_original = pygame.image.load('enemy.png')
food_image_original = pygame.image.load('food.png')
speed_image_original = pygame.image.load('speed.png')
freeze_image_original = pygame.image.load('freeze.png')
background_image_original = pygame.image.load("background.png")
gameclear_image_original = pygame.image.load("gameclear.png")

# 画像のサイズを設定
player_image = pygame.transform.scale(player_image_original, (50, 50))
enemy_image = pygame.transform.scale(enemy_image_original, (50, 50))
food_image = pygame.transform.scale(food_image_original, (30, 30))
speed_image = pygame.transform.scale(speed_image_original, (30, 30))
freeze_image = pygame.transform.scale(freeze_image_original, (30, 30))
background_image = pygame.transform.scale(background_image_original, (WIDTH, HEIGHT))
gameclear_image = pygame.transform.scale(gameclear_image_original, (WIDTH, HEIGHT))

# プレイヤー設定
player_pos = [WIDTH // 2, HEIGHT // 2]
player_size = 30
player_color = BLUE
health = 100

# 食べ物の設定
food_size = 20
food_pos = [random.randint(0, WIDTH - food_size), random.randint(0, HEIGHT - food_size)]
food_color = GREEN

# アイテム
item_size = 20
items = [{"pos": [random.randint(0, WIDTH), random.randint(0, HEIGHT)], "type": random.choice(["speed", "freeze"])} for _ in range(2)]
speed_boost_time = 0  # 移動速度アップの効果時間

# 敵
enemy_size = 30
enemies = [{"pos": [random.randint(0, WIDTH), random.randint(0, HEIGHT)], "speed": random.randint(2, 4)} for _ in range(5)]
enemy_color = RED
enemy_speed = 3
freeze_time = 0  # フリーズ効果の持続時間

# クリア条件(時間計測)
start_time = pygame.time.get_ticks()  # ゲーム開始時の時間
time_limit = 30000  # 30秒(ミリ秒)

#ゲームオーバー画面の表示関数
def game_over_screen():
    SCREEN.blit(background_image, (0, 0))
    font = pygame.font.Font(None, 72)
    text = font.render("GAME OVER", True, RED)
    SCREEN.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2 - 100))
    
#ゲームクリア画面の表示関数
def game_clear_screen():
    SCREEN.blit(gameclear_image, (0, 0))
    font = pygame.font.Font(None, 72)
    text = font.render("GAME CLEAR", True, WHITE)
    SCREEN.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2 - 100))

# 効果音
pygame.mixer.init()

eat_sound = pygame.mixer.Sound("eat.wav")
freeze_sound = pygame.mixer.Sound("freeze.wav")
speed_sound = pygame.mixer.Sound("speed.wav")
damage_sound = pygame.mixer.Sound("damage.wav")
gameclear_sound = pygame.mixer.Sound("gameclear.wav")

# ゲーム状態
game_state = "playing"  # "playing", "game_over", "game_clear"


clock = pygame.time.Clock()

# ゲームループ
running = True
while running:
    # 背景画像を描画
    SCREEN.blit(background_image, (0, 0))

    # イベント処理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    if game_state == "playing":
        # 経過時間計算
        elapsed_time = pygame.time.get_ticks() - start_time
        remaining_time = max(0, (time_limit - elapsed_time) // 1000)  # 残り時間(秒)

        # プレイヤー移動
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and player_pos[0] > 0:
            player_pos[0] -= player_speed
        if keys[pygame.K_RIGHT] and player_pos[0] < WIDTH - player_size:
            player_pos[0] += player_speed
        if keys[pygame.K_UP] and player_pos[1] > 0:
            player_pos[1] -= player_speed
        if keys[pygame.K_DOWN] and player_pos[1] < HEIGHT - player_size:
            player_pos[1] += player_speed

        # 食べ物の取得
        player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
        food_rect = pygame.Rect(food_pos[0], food_pos[1], food_size, food_size)
        if player_rect.colliderect(food_rect):
            health += 20  # 食べ物で体力回復
            food_pos = [random.randint(0, WIDTH - food_size), random.randint(0, HEIGHT - food_size)]
            # 効果音を鳴らす
            eat_sound.play()

        
        # 敵の移動と追尾
        if freeze_time <= 0:  # フリーズ効果がない場合のみ敵を動かす
            for enemy in enemies:
                if enemy["pos"][0] < player_pos[0]:
                    enemy["pos"][0] += enemy["speed"]
                if enemy["pos"][0] > player_pos[0]:
                    enemy["pos"][0] -= enemy["speed"]
                if enemy["pos"][1] < player_pos[1]:
                    enemy["pos"][1] += enemy["speed"]
                if enemy["pos"][1] > player_pos[1]:
                    enemy["pos"][1] -= enemy["speed"]

        # 敵との衝突判定
        for enemy in enemies:
            enemy_rect = pygame.Rect(enemy["pos"][0], enemy["pos"][1], enemy_size, enemy_size)
            if player_rect.colliderect(enemy_rect):
                health -= 1  # 衝突時に体力減少
                # 効果音を鳴らす
                damage_sound.play()
                if health <= 0:
                    game_state = "game_over"

        # アイテム取得
        for item in items:
            item_rect = pygame.Rect(item["pos"][0], item["pos"][1], item_size, item_size)
            if player_rect.colliderect(item_rect):
                if item["type"] == "speed":
                    speed_boost_time = 200  # 移動速度アップを200フレーム持続
                    player_speed = 8
                    # 効果音を鳴らす
                    speed_sound.play()
                elif item["type"] == "freeze":
                    freeze_time = 200  # フリーズ効果を200フレーム持続
                    # 効果音を鳴らす
                    freeze_sound.play()

                items.remove(item)  # アイテムを削除
                break

        # 効果時間の管理
        if speed_boost_time > 0:
            speed_boost_time -= 1
        else:
            player_speed = 5  # 通常速度に戻す

        if freeze_time > 0:
            freeze_time -= 1

        # 敵の描画
        for enemy in enemies:
            SCREEN.blit(enemy_image, (enemy["pos"][0], enemy["pos"][1]))

        # アイテムの描画
        for item in items:
            if item["type"] == "speed":
                SCREEN.blit(speed_image, (item["pos"][0], item["pos"][1]))
            elif item["type"] == "freeze":
                SCREEN.blit(freeze_image, (item["pos"][0], item["pos"][1]))

        # プレイヤーの描画
        SCREEN.blit(player_image, player_rect)  

        # 食べ物の描画
        SCREEN.blit(food_image, food_rect)

        # 体力の表示
        health_text = FONT.render(f"HP: {health}", True, WHITE)
        SCREEN.blit(health_text, (10, 10))

        #効果時間の表示
        if speed_boost_time > 0:
            speed_text = FONT.render("SPEED UP TIME!", True, RED)
            SCREEN.blit(speed_text, (10, 100))
        if freeze_time > 0:
            freeze_text = FONT.render("FREEZE!", True, BLUE)
            SCREEN.blit(freeze_text, (10, 70))

        #残り時間の表示
        timer_text = FONT.render(f"Time Left: {remaining_time} s", True, WHITE)
        SCREEN.blit(timer_text, (10, 40))

        # クリア判定
        if elapsed_time >= time_limit:
            # ゲームクリア音を鳴らす
            gameclear_sound.play()
            game_state = "game_clear"

    elif game_state == "game_over":
        # ゲームオーバー画面の表示
        game_over_screen()

    elif game_state == "game_clear":
        # ゲームクリア画面の表示
        game_clear_screen()

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

pygame.quit()
sys.exit()

▼ゲームプレイ画面

▼ゲームオーバー画面

▼ゲームクリア画面

すべてを実装すると、冒頭でお見せした通り本格的なゲームになりました。大満足です!!
プレイ画面
https://youtu.be/zPFta-tmf_0

6. まとめ

前回の作品ではChat GPTに教えてもらった通りにコードを書いて(ほぼ貼り付けるだけ。。)いましたが、徐々にこれまでの開発で使った知識をもとに自分で考えて書ける箇所もちらほら出てきました!!
成長です。

今後も他のゲーム製作に挑戦してみようと思います。

また、改善点やご意見などあればコメントお願いいたします!

7. 参考リンク

素材サイト
・無料 BGM・効果音のフリー音源素材 | Springin’ Sound Stock
https://www.springin.org/sound-stock/
・DOT ILLUST
https://dot-illust.net/
・ゲームまてりあるず
https://game-materials.com/

Discussion