【Python】迷路ゲームをPygameで作ってみた

プログラミング
広告
広告

皆さんこんにちは!

今回は、Pythonのpygameライブラリを使用して迷路ゲームを実装する方法を紹介します。

ゲームを作ることでプログラミングスキルも向上しますので、今回の記事を参考にしていただければと思います。

今回作成した迷路ゲームは、プレイヤーがスタート地点からゴール地点までたどり着くことを目的としたシンプルなゲームです。3つの階層があり、各階層をクリアすると次の階層に進みます。制限時間内にゴールにたどり着けないとゲームオーバーになります。

広告
広告

ゲーム画面

以下に作成したゲームの画面を表示します。

タイトル画面です
「始める」を押したらゲームが始まります。
時間切れになったらゲームオーバーになります

必要なライブラリのインストール

始めにpygameをインストールする必要があります。以下のコマンドを実行してインストールしてください。

pip install pygame

なおpygameの公式ドキュメントはこちらです。

コードの全体像

以下が、迷路ゲーム全体のコードです。このコードを実行すると、タイトル画面が表示され、「始める」ボタンを押すとゲームがスタートします。

プレイヤーは矢印キーまたはW,A,S,Dキーで移動でき、制限時間内にゴールに到達すると次の階層に進みます。

ランダム生成された迷路を3回ゴールしたらゲームクリアです。

import pygame
import sys
import random

# 初期設定
pygame.init()
screen_width = 1000  # 画面の幅
screen_height = 800  # 画面の高さ
cell_size = 40  # 各セル(迷路のブロック)のサイズ
cols = screen_width // cell_size  # 迷路の列数
rows = (screen_height - 100) // cell_size  # 迷路の行数(上部にスペースを確保)
white = (255, 255, 255)  # 白色
black = (0, 0, 0)  # 黒色
red = (255, 0, 0)  # 赤色
blue = (0, 0, 255)  # 青色

# フォント設定
pygame.font.init()
font_path = pygame.font.match_font('meiryo')  # 日本語フォントのパスを取得
title_font = pygame.font.Font(font_path, 74)  # タイトル用のフォント
text_font = pygame.font.Font(font_path, 36)  # テキスト用のフォント

# 迷路生成用の関数
def generate_maze(rows, cols):
    # 迷路を初期化(すべて壁で埋める)
    maze = [[1 for _ in range(cols)] for _ in range(rows)]
    start = (1, 1)  # スタート位置
    end = (random.randint(1, rows-2), random.randint(1, cols-2))  # ランダムなゴール位置
    stack = [start]  # 深さ優先探索のためのスタック

    # 深さ優先探索で迷路を生成
    while stack:
        current = stack[-1]
        y, x = current
        maze[y][x] = 0  # 現在位置を道に設定

        # 4方向の隣接セルをチェックして壁ならリストに追加
        neighbors = []
        if y > 2 and maze[y-2][x] == 1:
            neighbors.append((y-2, x))
        if y < rows - 3 and maze[y+2][x] == 1:
            neighbors.append((y+2, x))
        if x > 2 and maze[y][x-2] == 1:
            neighbors.append((y, x-2))
        if x < cols - 3 and maze[y][x+2] == 1:
            neighbors.append((y, x+2))

        if neighbors:
            # ランダムに次のセルを選択して道を作る
            next_cell = random.choice(neighbors)
            ny, nx = next_cell
            maze[(ny + y) // 2][(nx + x) // 2] = 0
            stack.append(next_cell)
        else:
            stack.pop()  # 戻る

    # スタートとゴールの位置を道に設定
    maze[1][1] = 0
    maze[end[0]][end[1]] = 0

    # 外周を壁で覆う
    for i in range(rows):
        maze[i][0] = 1
        maze[i][cols-1] = 1
    for i in range(cols):
        maze[0][i] = 1
        maze[rows-1][i] = 1

    return maze, start, end

# タイトル画面を表示する関数
def show_title_screen(screen):
    # タイトルテキストを描画
    text = title_font.render('迷路ゲーム', True, blue)
    start_button = pygame.Rect(screen_width // 2 - 100, screen_height // 2, 200, 50)
    button_text = text_font.render('始める', True, white)
    while True:
        screen.fill(black)
        screen.blit(text, (screen_width // 2 - text.get_width() // 2, screen_height // 2 - 100))
        pygame.draw.rect(screen, red, start_button)
        screen.blit(button_text, (start_button.x + (start_button.width - button_text.get_width()) // 2, start_button.y + (start_button.height - button_text.get_height()) // 2))

        # イベント処理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # ウィンドウの閉じるボタンが押された場合
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:  # マウスボタンが押された場合
                if start_button.collidepoint(event.pos):  # スタートボタンがクリックされた場合
                    return  # ゲーム開始

        pygame.display.flip()

# メインゲーム関数
def main_game():
    screen = pygame.display.set_mode((screen_width, screen_height))
    pygame.display.set_caption('迷路ゲーム')
    clock = pygame.time.Clock()

    level = 1
    max_level = 3

    while level <= max_level:
        maze, start, end = generate_maze(rows, cols)
        player_pos = list(start)  # プレイヤーの初期位置
        start_time = pygame.time.get_ticks()  # ゲーム開始時間

        while True:
            elapsed_time = (pygame.time.get_ticks() - start_time) // 1000
            remaining_time = 30 - elapsed_time  # 残り時間を30秒に設定

            if remaining_time <= 0:
                show_game_over(screen, '時間切れ!ゲームオーバー')
                return

            # イベント処理
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    # 矢印キーとWASDキーでプレイヤーを移動
                    if (event.key == pygame.K_LEFT or event.key == pygame.K_a) and player_pos[1] > 0 and maze[player_pos[0]][player_pos[1] - 1] == 0:
                        player_pos[1] -= 1
                    elif (event.key == pygame.K_RIGHT or event.key == pygame.K_d) and player_pos[1] < cols - 1 and maze[player_pos[0]][player_pos[1] + 1] == 0:
                        player_pos[1] += 1
                    elif (event.key == pygame.K_UP or event.key == pygame.K_w) and player_pos[0] > 0 and maze[player_pos[0] - 1][player_pos[1]] == 0:
                        player_pos[0] -= 1
                    elif (event.key == pygame.K_DOWN or event.key == pygame.K_s) and player_pos[0] < rows - 1 and maze[player_pos[0] + 1][player_pos[1]] == 0:
                        player_pos[0] += 1

            screen.fill(black)

            # 迷路を描画
            for y in range(rows):
                for x in range(cols):
                    color = white if maze[y][x] == 1 else black
                    pygame.draw.rect(screen, color, (x * cell_size, y * cell_size + 100, cell_size, cell_size))

            # ゴール地点を描画
            pygame.draw.rect(screen, red, (end[1] * cell_size, end[0] * cell_size + 100, cell_size, cell_size))
            # プレイヤーを描画
            pygame.draw.rect(screen, blue, (player_pos[1] * cell_size, player_pos[0] * cell_size + 100, cell_size, cell_size))

            # 現在の階層を表示
            level_text = text_font.render(f'階層: {level}', True, white)
            screen.blit(level_text, (10, 10))

            # 残り時間を表示
            timer_text = text_font.render(f'残り時間: {remaining_time}', True, white)
            screen.blit(timer_text, (10, 50))

            # プレイヤーがゴールに到達した場合
            if player_pos == list(end):
                level += 1
                break

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

    # 全ての階層をクリアした場合
    show_game_over(screen, 'おめでとう!ゴールにつきました!')

# ゲームオーバー画面を表示する関数
def show_game_over(screen, message):
    text = text_font.render(message, True, red)
    while True:
        screen.fill(black)
        screen.blit(text, (screen_width // 2 - text.get_width() // 2, screen_height // 2 - text.get_height() // 2))
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

# ゲーム開始
if __name__ == '__main__':
    screen = pygame.display.set_mode((screen_width, screen_height))
    show_title_screen(screen)  # タイトル画面を表示
    main_game()  # メインゲームを開始

主要な部分のコード説明

ここでは、コードの主要な部分をピックアップして説明します。

初期設定

まず、Pygameを初期化し、ゲーム画面の幅や高さ、迷路の各セルのサイズ、色の設定を行います。また、日本語フォントを指定してタイトル用とテキスト用のフォントを設定します。

pygame.init()
screen_width = 1000  # 画面の幅
screen_height = 800  # 画面の高さ
cell_size = 40  # 各セル(迷路のブロック)のサイズ
cols = screen_width // cell_size  # 迷路の列数
rows = (screen_height - 100) // cell_size  # 迷路の行数(上部にスペースを確保)
white = (255, 255, 255)  # 白色
black = (0, 0, 0)  # 黒色
red = (255, 0, 0)  # 赤色
blue = (0, 0, 255)  # 青色

# フォント設定
pygame.font.init()
font_path = pygame.font.match_font('meiryo')  # 日本語フォントのパスを取得
title_font = pygame.font.Font(font_path, 74)  # タイトル用のフォント
text_font = pygame.font.Font(font_path, 36)  # テキスト用のフォント

迷路生成関数

この関数では、迷路を深さ優先探索アルゴリズムを用いて生成します。迷路は初期化時にすべて壁(1)で埋め、スタート位置から道(0)を掘り進めます。また、ランダムな位置にゴールを設定し、外周を壁で囲みます。

def generate_maze(rows, cols):
    # 迷路を初期化(すべて壁で埋める)
    maze = [[1 for _ in range(cols)] for _ in range(rows)]
    start = (1, 1)  # スタート位置
    end = (random.randint(1, rows-2), random.randint(1, cols-2))  # ランダムなゴール位置
    stack = [start]  # 深さ優先探索のためのスタック

    # 深さ優先探索で迷路を生成
    while stack:
        current = stack[-1]
        y, x = current
        maze[y][x] = 0  # 現在位置を道に設定

        # 4方向の隣接セルをチェックして壁ならリストに追加
        neighbors = []
        if y > 2 and maze[y-2][x] == 1:
            neighbors.append((y-2, x))
        if y < rows - 3 and maze[y+2][x] == 1:
            neighbors.append((y+2, x))
        if x > 2 and maze[y][x-2] == 1:
            neighbors.append((y, x-2))
        if x < cols - 3 and maze[y][x+2] == 1:
            neighbors.append((y, x+2))

        if neighbors:
            # ランダムに次のセルを選択して道を作る
            next_cell = random.choice(neighbors)
            ny, nx = next_cell
            maze[(ny + y) // 2][(nx + x) // 2] = 0
            stack.append(next_cell)
        else:
            stack.pop()  # 戻る

    # スタートとゴールの位置を道に設定
    maze[1][1] = 0
    maze[end[0]][end[1]] = 0

    # 外周を壁で覆う
    for i in range(rows):
        maze[i][0] = 1
        maze[i][cols-1] = 1
    for i in range(cols):
        maze[0][i] = 1
        maze[rows-1][i] = 1

    return maze, start, end

タイトル画面表示関数

この関数では、タイトル画面を表示し、ユーザーが「始める」ボタンをクリックするとゲームが開始されます。画面上にタイトルテキストとボタンを描画し、クリックイベントを検出します。

def show_title_screen(screen):
    # タイトルテキストを描画
    text = title_font.render('迷路ゲーム', True, blue)
    start_button = pygame.Rect(screen_width // 2 - 100, screen_height // 2, 200, 50)
    button_text = text_font.render('始める', True, white)
    while True:
        screen.fill(black)
        screen.blit(text, (screen_width // 2 - text.get_width() // 2, screen_height // 2 - 100))
        pygame.draw.rect(screen, red, start_button)
        screen.blit(button_text, (start_button.x + (start_button.width - button_text.get_width()) // 2, start_button.y + (start_button.height - button_text.get_height()) // 2))

        # イベント処理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # ウィンドウの閉じるボタンが押された場合
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:  # マウスボタンが押された場合
                if start_button.collidepoint(event.pos):  # スタートボタンがクリックされた場合
                    return  # ゲーム開始

        pygame.display.flip()

メインゲーム関数

この関数がゲームのメインループです。迷路の生成、プレイヤーの初期位置設定、時間の計測を行います。プレイヤーは矢印キーやW,A,S,Dキーで移動し、ゴールにたどり着くと次の階層に進みます。制限時間を超えるとゲームオーバーになります。

def main_game():
    screen = pygame.display.set_mode((screen_width, screen_height))
    pygame.display.set_caption('迷路ゲーム')
    clock = pygame.time.Clock()

    level = 1
    max_level = 3

    while level <= max_level:
        maze, start, end = generate_maze(rows, cols)
        player_pos = list(start)  # プレイヤーの初期位置
        start_time = pygame.time.get_ticks()  # ゲーム開始時間

        while True:
            elapsed_time = (pygame.time.get_ticks() - start_time) // 1000
            remaining_time = 30 - elapsed_time  # 残り時間を30秒に設定

            if remaining_time <= 0:
                show_game_over(screen, '時間切れ!ゲームオーバー')
                return

            # イベント処理
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    # 矢印キーとWASDキーでプレイヤーを移動
                    if (event.key == pygame.K_LEFT or event.key == pygame.K_a) and player_pos[1] > 0 and maze[player_pos[0]][player_pos[1] - 1] == 0:
                        player_pos[1] -= 1
                    elif (event.key == pygame.K_RIGHT or event.key == pygame.K_d) and player_pos[1] < cols - 1 and maze[player_pos[0]][player_pos[1] + 1] == 0:
                        player_pos[1] += 1
                    elif (event.key == pygame.K_UP or event.key == pygame.K_w) and player_pos[0] > 0 and maze[player_pos[0] - 1][player_pos[1]] == 0:
                        player_pos[0] -= 1
                    elif (event.key == pygame.K_DOWN or event.key == pygame.K_s) and player_pos[0] < rows - 1 and maze[player_pos[0] + 1][player_pos[1]] == 0:
                        player_pos[0] += 1

            screen.fill(black)

            # 迷路を描画
            for y in range(rows):
                for x in range(cols):
                    color = white if maze[y][x] == 1 else black
                    pygame.draw.rect(screen, color, (x * cell_size, y * cell_size + 100, cell_size, cell_size))

            # ゴール地点を描画
            pygame.draw.rect(screen, red, (end[1] * cell_size, end[0] * cell_size + 100, cell_size, cell_size

))
            # プレイヤーを描画
            pygame.draw.rect(screen, blue, (player_pos[1] * cell_size, player_pos[0] * cell_size + 100, cell_size, cell_size))

            # 現在の階層を表示
            level_text = text_font.render(f'階層: {level}', True, white)
            screen.blit(level_text, (10, 10))

            # 残り時間を表示
            timer_text = text_font.render(f'残り時間: {remaining_time}', True, white)
            screen.blit(timer_text, (10, 50))

            # プレイヤーがゴールに到達した場合
            if player_pos == list(end):
                level += 1
                break

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

    # 全ての階層をクリアした場合
    show_game_over(screen, 'おめでとう!ゴールにつきました!')

ゲームオーバー画面表示関数

この関数では、ゲームオーバー画面を表示します。メッセージを画面中央に表示し、ウィンドウが閉じられるまで待機します。

def show_game_over(screen, message):
    text = text_font.render(message, True, red)
    while True:
        screen.fill(black)
        screen.blit(text, (screen_width // 2 - text.get_width() // 2, screen_height // 2 - text.get_height() // 2))
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

最後に

この迷路ゲームは、PythonとPygameを使ってシンプルかつ効果的にゲームを作成する方法を紹介しました。

迷路のランダム生成、プレイヤーの移動、制限時間の設定など、基本的なゲーム開発の概念を学ぶのに最適です。このコードを基に、さらに機能を追加したり、デザインを変更して独自のゲームを作成してみてください。ゲーム開発の楽しさをぜひ体験してみてください!

また、Pythonを使ったゲーム作成方法をもっと学びたい方は以下の本を参考にしてください。
内容もわかりやすく実践的で良本です。
プログラミング未経験者にもおすすめの本ですよ。

Pythonではじめるゲーム制作 超入門 知識ゼロからのプログラミング&アルゴリズムと数学

新品価格
¥2,376から
(2024/6/11 11:45時点)

人気ブログランキング

クリックするとブログランキングサイトに移動します。

にほんブログ村 IT技術ブログへ
インターネット・コンピュータランキング
広告
広告
プログラミング
広告
technyankoをフォローする
広告
プロフィール
technyanko

元情シス・SEです。
当ブログではPCに関して困ったことや役立つ情報を発信していきます。
たまにバッチスクリプトやPythonに関する記事も投稿します。

technyankoをフォローする
広告
タイトルとURLをコピーしました