🦸

【Python】プログラミング初心者がChatGPTを活用して、ゲーム開発した話~キャッチゲーム編~ 初心者向けガイドとチュートリアル

2024/11/23に公開

1. はじめに

Pythonはプログラミング初心者に人気の言語のひとつですが、Pythonと聞いてどのようなイメージを思い浮かべますか?
例えば、、AI、自動化、データ分析などが上がると思います。

実はPythonを使って手軽にゲーム開発に挑戦することもできます。
そこで今回はChat GPTを活用しながらゲーム開発に挑戦しました🎮

本記事はPythonを使ったゲームを作りたいけど、何から始めていいかわからない人のためのガイドです!
必要なライブラリのインストール方法から始め、簡単なゲームのサンプルコードをご紹介します。

先に完成品をお見せするとこんな感じです!
https://youtu.be/jj-we0wWWSo

私もまだまだプログラミング勉強中ですが、簡単にゲームを作ることができました!
今回はシンプルなキャッチゲームです。

この記事は以下に当てはまる方を対象としています。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. ゲーム設計を考えよう

今回は勇者であるプレイヤーが上から降ってくるスターを落とさないようにキャッチするシンプルなゲームを作成します。

▼イメージ画面

ルールは簡単です。
プレイヤーがスターをキャッチするとスコアが加算され、スターを落としてしまうとゲームオーバーです。
また、スコアに応じてスターの出現頻度が高くなり、難易度が上がります。


必要な要素は以下のとおりです。

  • 勇者(プレイヤー)
  • スター(フルーツ的な存在)
  • スコアシステム
  • ゲームオーバー条件
  • 効果音システム

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

  1. プレイヤーを矢印キーで操作
  2. 上からスターが降ってくる
  3. プレイヤーがスターのキャッチに成功すると、スターが消えスコア加算
  4. スターが地面に落ちるとゲームオーバー

4. コードを書こう!

1. 基本の構造を準備します。

はじめにChatGPTでフルーツキャッチゲームの基本的なコードを生成してもらいます。

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

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

# Pygameの初期化
pygame.init()

# 画面サイズとFPS設定
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("フルーツキャッチゲーム")
clock = pygame.time.Clock()

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

# ゲームループ用変数
running = True

# プレイヤークラス
class Player:
    def __init__(self):
        self.width = 100
        self.height = 20
        self.x = SCREEN_WIDTH // 2 - self.width // 2
        self.y = SCREEN_HEIGHT - 50
        self.speed = 10

    def move(self, keys):
        if keys[pygame.K_LEFT] and self.x > 0:
            self.x -= self.speed
        if keys[pygame.K_RIGHT] and self.x < SCREEN_WIDTH - self.width:
            self.x += self.speed

    def draw(self):
        pygame.draw.rect(screen, RED, (self.x, self.y, self.width, self.height))

# フルーツクラス
class Fruit:
    def __init__(self):
        self.size = 20
        self.x = random.randint(0, SCREEN_WIDTH - self.size)
        self.y = 0
        self.speed = random.randint(3, 6)

    def fall(self):
        self.y += self.speed

    def draw(self):
        pygame.draw.ellipse(screen, BLACK, (self.x, self.y, self.size, self.size))

#変数の作成
player = Player()
fruits = []
score = 0

font = pygame.font.Font(None, 36)

# ゲームループ
while running:
    screen.fill(WHITE)

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

    # プレイヤーの動き
    keys = pygame.key.get_pressed()
    player.move(keys)

    # フルーツの生成
    if random.randint(1, 30) == 1:  # ランダムにフルーツ生成
        fruits.append(Fruit())

    # フルーツの更新と描画
    for fruit in fruits[:]:
        fruit.fall()
        fruit.draw()

        # キャッチ判定
        if (player.y < fruit.y + fruit.size and
                player.x < fruit.x + fruit.size and
                player.x + player.width > fruit.x):
            fruits.remove(fruit)
            score += 1

        # 地面に落ちたらゲームオーバー
        elif fruit.y > SCREEN_HEIGHT:
            running = False

    # プレイヤーの描画
    player.draw()

    # スコア表示
    score_text = font.render(f"Score: {score}", True, BLACK)
    screen.blit(score_text, (10, 10))

    # 画面更新
    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()
sys.exit()


実行結果です。

赤色がプレイヤー、黒色の丸がフルーツを表しています。
問題なくゲームを実行することできました!

ただし、落ちてくるスピードが速すぎてすぐゲームオーバーになってしまいます。。。ハードモードすぎる、、
残念ながらこれではゲームとして成り立たないです。
そのため、以下の4点を実装することにしました。

・背景とプレイヤーとフルーツに画像を設定
・ゲームオーバー画面の表示
・キャッチ時とゲームオーバー時に効果音を絶対
・落下スピードと量の調整

もちろん追加の機能もコードもChat GPTに聞きながら実装しました。
こんな感じで聞くとソースコードを教えてくれます✨

この後の流れとしては、作成された追加機能のコードを先ほどの基本コードに実装していきます。


2. 背景とプレイヤーとフルーツに画像を設定

<手順>

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

https://dot-illust.net/tag/uchu/

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

  1. 画像ファイルを読み込むコード、画像サイズを設定するコードを追加します。
#画像を読み込む
player_image_original = pygame.image.load('player.png')
fruit_image_original = pygame.image.load('fruit.png')
background_image_original = pygame.image.load("background.png")

# 画像のサイズを設定
player_image = pygame.transform.scale(player_image_original, (50, 50))
fruit_image = pygame.transform.scale(fruit_image_original, (30, 30))
background_image = pygame.transform.scale(background_image_original, (SCREEN_WIDTH, SCREEN_HEIGHT))
  1. プレイヤークラス、フルーツクラス内に画像を描画するコードを追加します。
    プレイヤークラス内のdraw(self)関数を修正
# プレイヤーの描画
    def draw(self):
        screen.blit(player_image, (self.x, self.y))  

フルーツクラス内のdraw(self)関数を修正

# フルーツの描画
    def draw(self):
        screen.blit(fruit_image, (self.x, self.y))
  1. ゲームループ内のscreen.fill(WHITE)を背景画像を描画する以下のコードに修正します。
# ゲームループ
while running:
    # 背景画像を描画
    screen.blit(background_image, (0, 0))

3. ゲームオーバー画面の表示

<手順>

  1. ゲームオーバー画面の表示関数を作成します。
#ゲームオーバー画面の表示関数
def game_over_screen():
    screen.blit(background_image, (0, 0))
    font = pygame.font.Font(None, 72)
    text = font.render("GAME OVER", True, RED)
    score_text = font.render(f"Your Score: {score}", True, WHITE)
    screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, SCREEN_HEIGHT // 2 - 100))
    screen.blit(score_text, (SCREEN_WIDTH // 2 - score_text.get_width() // 2, SCREEN_HEIGHT // 2))
  1. ゲームオーバーの変数を作成します。
#変数の作成
player = Player()
fruits = []
score = 0
game_over = False
  1. ゲームループ内でフルーツが落ちた時に、game_over変数をTrueにするようにrunning = Falseの部分を修正します。
# 地面に落ちたらゲームオーバー
elif fruit.y > SCREEN_HEIGHT:
    game_over = True
  1. ゲームループ内でgame_over = Trueの時に、関数game_over_screen()を実行するようにIf-else文を追加します。
    if not game_over:
        # プレイヤーの動き
        keys = pygame.key.get_pressed()
        player.move(keys)

        # フルーツの生成
        if random.randint(1, 30) == 1:  # ランダムにフルーツ生成
            fruits.append(Fruit())

        # フルーツの更新と描画
        for fruit in fruits[:]:
            fruit.fall()
            fruit.draw()

            # キャッチ判定
            if (player.y < fruit.y + fruit.size and
                    player.x < fruit.x + fruit.size and
                    player.x + player.width > fruit.x):
                fruits.remove(fruit)
                score += 1

            # 地面に落ちたらゲームオーバー
            elif fruit.y > SCREEN_HEIGHT:
                game_over = True

        # プレイヤーの描画
        player.draw()

        # スコア表示
        score_text = font.render(f"Score: {score}", True, BLACK)
        screen.blit(score_text, (10, 10))

    else:
        # ゲームオーバー画面の表示
        game_over_screen()

4. キャッチ時とゲームオーバー時に効果音を出す

<手順>

  1. 効果音のmp3ファイルをVisual Studio Code上にアップロードし、wav形式にRenameします。
シナリオ 音源名
キャッチ時 8bit獲得6
ゲームオーバー時 8bit失敗3

https://www.springin.org/sound-stock/category/machine/page/3/

▼参考画像

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

# サウンドの読み込み
catch_sound = pygame.mixer.Sound("catch.wav")
game_over_sound = pygame.mixer.Sound("game_over.wav")
  1. ゲームループ内でフルーツをキャッチした時に効果音を再生するコードを追加します。
# キャッチ判定
            if (player.y < fruit.y + fruit.size and
                    player.x < fruit.x + fruit.size and
                    player.x + player.width > fruit.x):
                fruits.remove(fruit)
                score += 10
                catch_sound.play()  # キャッチサウンドを再生
  1. ゲームループ内でゲームオーバー時に効果音を再生するコードを追加します。
# 地面に落ちたらゲームオーバー
            elif fruit.y > SCREEN_HEIGHT:
                game_over = True
                game_over_sound.play()  # ゲームオーバーサウンドを再生

5. 落下スピードと量の調整

<手順>

  1. フルーツクラス内のself.speed = random.randint(3, 6)を修正し、落下スピードを遅くします。
# フルーツクラス
class Fruit:
    def __init__(self):
        self.size = 20
        self.x = random.randint(0, SCREEN_WIDTH - self.size)
        self.y = 0
        self.speed = random.randint(1, 3)
  1. ゲームループ内でスコアに応じてフルーツの生成頻度を高くするコード※を追加します。
# フルーツの生成
        # 基本の頻度(1/30)
        spawn_chance = max(30 - score // 50, 10)  # スコアが上がると頻度が高くなる(最大1/10)
        if len(fruits) < 5 and random.randint(1, spawn_chance) == 1:  # 最大5個までランダムで生成
            fruits.append(Fruit())

※補足

  • spawn_chance:スコア0の初期状態では30となりフルーツの生成確率は約1/30
    スコアが50増えるごとにspawn_chanceが1減少(例: スコアが500の場合、spawn_chance = 20)
    生成頻度が高くなり、ゲームが徐々に難しくなります。
  • max()関数:最低でもspawn_chance = 10(最大難易度は生成確率1/10)
  • len(fruits) < 5: 画面上のフルーツが最大5個になるように制限

5. 完成!

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

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

# Pygameの初期化
pygame.init()

# サウンドの初期化
pygame.mixer.init()

# 画面サイズとFPS設定
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("キャッチゲーム")
clock = pygame.time.Clock()

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

#画像を読み込む
player_image_original = pygame.image.load('player.png')
fruit_image_original = pygame.image.load('fruit.png')
background_image_original = pygame.image.load("background.png")

# 画像のサイズを設定
player_image = pygame.transform.scale(player_image_original, (50, 50))
fruit_image = pygame.transform.scale(fruit_image_original, (30, 30))
background_image = pygame.transform.scale(background_image_original, (SCREEN_WIDTH, SCREEN_HEIGHT))

# サウンドの読み込み
catch_sound = pygame.mixer.Sound("catch.wav")
game_over_sound = pygame.mixer.Sound("game_over.wav")

# ゲームループ用変数
running = True

# プレイヤークラス
class Player:
    # プレイヤー設定
    def __init__(self):
        self.width = 50
        self.height = 50
        self.x = SCREEN_WIDTH // 2 - self.width // 2
        self.y = SCREEN_HEIGHT - 50
        self.speed = 10

    # プレイヤーの移動
    def move(self, keys):
        if keys[pygame.K_LEFT] and self.x > 0:
            self.x -= self.speed
        if keys[pygame.K_RIGHT] and self.x < SCREEN_WIDTH - self.width:
            self.x += self.speed

    # プレイヤーの描画
    def draw(self):
        screen.blit(player_image, (self.x, self.y))  

# フルーツクラス
class Fruit:
    # フルーツ設定
    def __init__(self):
        self.size = 30
        self.x = random.randint(0, SCREEN_WIDTH - self.size)
        self.y = 0
        self.speed = random.randint(1,  3)

    # フルーツの落下
    def fall(self):
        self.y += self.speed

    # フルーツの描画
    def draw(self):
        screen.blit(fruit_image, (self.x, self.y))

#ゲームオーバー画面の表示関数
def game_over_screen():
    screen.blit(background_image, (0, 0))
    font = pygame.font.Font(None, 72)
    text = font.render("GAME OVER", True, RED)
    score_text = font.render(f"Your Score: {score}", True, WHITE)
    screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, SCREEN_HEIGHT // 2 - 100))
    screen.blit(score_text, (SCREEN_WIDTH // 2 - score_text.get_width() // 2, SCREEN_HEIGHT // 2))
    
#変数の作成
player = Player()
fruits = []
score = 0
game_over = False

font = pygame.font.Font(None, 36)

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

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

    if not game_over:
        # プレイヤーの動き
        keys = pygame.key.get_pressed()
        player.move(keys)

        # フルーツの生成
        # 基本の頻度(1/30)
        spawn_chance = max(30 - score // 50, 10)  # スコアが上がると頻度が高くなる(最大1/10)
        if len(fruits) < 5 and random.randint(1, spawn_chance) == 1:  # 最大5個までランダムで生成
            fruits.append(Fruit())

        # フルーツの更新と描画
        for fruit in fruits[:]:
            fruit.fall()
            fruit.draw()

            # キャッチ判定
            if (player.y < fruit.y + fruit.size and
                    player.x < fruit.x + fruit.size and
                    player.x + player.width > fruit.x):
                fruits.remove(fruit)
                score += 10
                catch_sound.play()  # キャッチサウンドを再生

            # 地面に落ちたらゲームオーバー
            elif fruit.y > SCREEN_HEIGHT:
                game_over = True
                game_over_sound.play()  # ゲームオーバーサウンドを再生

        # プレイヤーの描画
        player.draw()

        # スコア表示
        score_text = font.render(f"Score: {score}", True, WHITE)
        screen.blit(score_text, (10, 10))

    else:
        # ゲームオーバー画面の表示
        game_over_screen()

    # 画面更新
    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()
sys.exit()

▼ゲームプレイ画面

▼ゲームオーバー画面

すべてを実装すると、冒頭でお見せした通り本格的なゲームになりました。大満足です!!

今後の改良アイデアとしては、スコアに応じてプレイヤーが進化したり敵を追加するなどしたら、もっとゲーム性が増すかなと思いました。
他にも改良アイデアがあればコメント欄で教えていただけるとうれしいです!

6. まとめ

今回もChatGPTを使ったゲーム開発に挑戦しました。
実際に開発することで、プログラミング学習を楽しく続けられるのはいいね。
今後は他のゲーム製作に挑戦してみようと思います。

最後まで読んでくださりありがとうございました。
少しでも参考になる情報となっていれば幸いです。

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

7. 参考リンク

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

Discussion