パズドラ風ゲームを作ってみた 開発の道のりとコードを徹底解説【Python】

もちとかげ IT関連

はじめに

この記事でわかること

  • 完成したパズドラ風ゲームの基本的なルールと遊び方
  • ゲームを構成する主要な機能(パズルの移動、マッチング、消去、落下、生成)
  • 開発に使用したプログラミング言語とライブラリの紹介
  • 各機能がどのように実装されているかの概要
  • 今後の展望や改善点

1. 今回作成したもの

さて、早速ですが、完成したパズドラ風ゲームがどんなものかをご紹介しましょう!

画面には、カラフルなパズルのマス目が広がっています。これは、皆さんがよく知っているパズドラのように、同じ色のパズルを縦や横に3つ以上揃えて消していくゲームです。

今回の完成版では、基本的なパズルの移動、揃ったパズルの消去、そして消えた場所への新しいパズルの落下と生成といった、ゲームの核となる部分を実装しました。

まだ、スコア表示や特別なスキルといった要素はありませんが、パズルを動かして連鎖を狙う楽しさは十分に体験できるはずです。

2. ゲームの基本的なルールと遊び方

このゲームのルールはシンプルです。

  1. パズルの移動: 画面に表示されたパズルを、マウスを使って上下左右に隣り合ったパズルと入れ替えることができます。
  2. パズルの消去: 同じ色のパズルが縦または横に3つ以上並ぶと、それらのパズルは消えます。
  3. 連鎖: パズルが消えることで、さらに他のパズルが3つ以上並ぶと、連鎖的に消えていきます。
  4. 新しいパズルの生成: 消えたパズルの上には、新しいパズルが上から落ちてきます。もし空いたスペースがあれば、そこにも新しいパズルがランダムに生成されます。

基本的な遊び方は、マウスを使ってパズルを動かし、3つ以上同じ色を揃えて消していく、これだけです!連鎖が起きると、画面が賑やかになり、とても爽快ですよ!

3. ゲームを支える主要な機能たち

このゲームがどのように動いているのか、主要な機能を一つずつ見ていきましょう。

3.1. パズルの生成と初期配置

ゲームが始まったとき、画面にはランダムな色のパズルが配置されます。これは、create_grid() という関数が行っています。

def create_grid():
    grid = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
    for y in range(GRID_HEIGHT):
        for x in range(GRID_WIDTH):
            puzzle_type = random.choice(PUZZLE_TYPES)
            grid[y][x] = Puzzle(x, y, puzzle_type)
    while find_matches(grid): # 初期配置でマッチがある場合は再生成
        grid = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
        for y in range(GRID_HEIGHT):
            for x in range(GRID_WIDTH):
                puzzle_type = random.choice(PUZZLE_TYPES)
                grid[y][x] = Puzzle(x, y, puzzle_type)
    return grid

このコードでは、まず空のマス目(None)でできたゲームの盤面(grid)を作ります。そして、それぞれのマス目に、あらかじめ定義された色の種類(PUZZLE_TYPES)の中からランダムに選ばれたパズルを配置しています。

注目すべきは、while find_matches(grid): の部分です。これは、初期配置でいきなりパズルが揃ってしまっている状態を避けるために、もし揃っている部分があれば、もう一度盤面を作り直すという処理を行っています。こうすることで、ゲーム開始時から連鎖が始まってしまうのを防いでいるんですね。

初期画面

3.2. パズルの移動

ゲームの醍醐味であるパズルの移動は、マウスの操作によって行われます。main.pyMOUSEBUTTONDOWN イベントと MOUSEBUTTONUP イベント、そして MOUSEMOTION イベントが連携して、この機能を支えています。

  • MOUSEBUTTONDOWN: マウスのボタンが押されたときに、どのパズルが選択されたかを記録します。
  • MOUSEBUTTONUP: マウスのボタンが離されたときに、移動が完了したと判断し、パズルの入れ替え処理 (swap_grid_positions()) を行います。
  • MOUSEMOTION: マウスがドラッグされている間、選択されたパズルをマウスの動きに合わせて表示します。また、一定の距離以上移動したら、隣のパズルとの入れ替えを試みます。
elif event.type == pygame.MOUSEMOTION:
    if dragging_puzzle and drag_start_pos:
        current_pos = pygame.mouse.get_pos()
        dx = current_pos[0] - drag_start_pos[0]
        dy = current_pos[1] - drag_start_pos[1]

        abs_dx = abs(dx)
        abs_dy = abs(dy)

        if current_time - last_swap_time > SWAP_INTERVAL:
            swap_occurred = False
            current_grid_pos = get_grid_pos_from_mouse(current_pos)

            if abs_dx > CELL_SIZE // 2 and abs_dx > abs_dy: # 左右方向の移動
                direction = "right" if dx > 0 else "left"
                next_pos = get_adjacent_pos(initial_grid_pos, direction)
                # ... (左右のスワップ処理) ...
            elif abs_dy > CELL_SIZE // 2 and abs_dy > abs_dx: # 上下方向の移動
                direction = "down" if dy > 0 else "up"
                next_pos = get_adjacent_pos(initial_grid_pos, direction)
                # ... (上下のスワップ処理) ...
            # ...

ここでは、マウスが動いた距離に応じて、左右または上下の隣接するマスとパズルを入れ替える処理を行っています。SWAP_INTERVAL という変数で、連続してパズルを入れ替えられる間隔を調整することで、誤操作を防ぐ工夫もされています。

3.3. パズルのマッチングと消去

パズルを移動させた後や、新しいパズルが落ちてきたときには、同じ色のパズルが3つ以上並んでいるかを確認する必要があります。これを行うのが find_matches_after_move() 関数です。

def find_matches_after_move(grid):
    matches = []
    # 横方向のチェック
    for y in range(GRID_HEIGHT):
        for x in range(GRID_WIDTH - 2):
            if grid[y][x] and grid[y][x+1] and grid[y][x+2] and \
               grid[y][x].type == grid[y][x+1].type == grid[y][x+2].type:
                match = [(y, x), (y, x+1), (y, x+2)]
                for i in range(x + 3, GRID_WIDTH):
                    if grid[y][i] and grid[y][i].type == grid[y][x].type:
                        match.append((y, i))
                    else:
                        break
                if len(match) >= 3:
                    matches.extend(match)

    # 縦方向のチェック
    for x in range(GRID_WIDTH):
        for y in range(GRID_HEIGHT - 2):
            if grid[y][x] and grid[y+1][x] and grid[y+2][x] and \
               grid[y][x].type == grid[y+1][x].type == grid[y+2][x].type:
                match = [(y, x), (y+1, x), (y+2, x)]
                for i in range(y + 3, GRID_HEIGHT):
                    if grid[i][x] and grid[i][x].type == grid[y][x].type:
                        match.append((i, x))
                    else:
                        break
                if len(match) >= 3:
                    matches.extend(match)

    return list(set(matches))

この関数は、盤面全体を調べて、横方向と縦方向に同じ色のパズルが連続して3つ以上並んでいる場所を見つけ出し、その座標をリスト (matches) に保存して返します。同じ場所が重複して記録されないように、最後に set() で重複を削除しています。

見つかった揃ったパズルは、clear_matches() 関数によって盤面から None に置き換えられ、消去されます。

def clear_matches(grid, grid_positions):
    """指定された位置のパズルをグリッドからNoneにする"""
    for y, x in grid_positions:
        grid[y][x] = None

3.4. パズルの落下と新しいパズルの生成

パズルが消えた場所には、上にあるパズルが落ちてくる必要があります。これを行うのが drop_puzzles() 関数です。

def drop_puzzles(grid):
    """空のスペースの上にいるパズルを落下させる"""
    for x in range(GRID_WIDTH):
        empty_row = -1
        for y in range(GRID_HEIGHT - 1, -1, -1):
            if grid[y][x] is None and empty_row == -1:
                empty_row = y
            elif grid[y][x] and empty_row != -1:
                puzzle = grid[y][x]
                grid[empty_row][x] = puzzle
                grid[y][x] = None
                puzzle.start_fall(y * CELL_SIZE) # 落下アニメーションを開始
                puzzle.y = empty_row
                empty_row -= 1

この関数では、各列ごとに下から上に向かって空いているマス目 (None) を探します。もし空いているマス目の上にパズルがあれば、そのパズルを空いているマス目に移動させ、元の場所を None にします。

ここで注目したいのは、puzzle.start_fall(y * CELL_SIZE) の部分です。これは、パズルが落下する際に、落下アニメーションを開始するための処理を呼び出しています。パズルはただ瞬間移動するのではなく、きちんとアニメーションしながら落ちてくるように作られているんですね。

そして、generate_new_puzzles() 関数は、盤面の空いているマス目(None のままになっている場所)に、新しいパズルをランダムに生成します。

def generate_new_puzzles(grid):
    """空のスペースに新しいパズルを生成する"""
    for x in range(GRID_WIDTH):
        for y in range(GRID_HEIGHT):
            if grid[y][x] is None:
                puzzle_type = random.choice(PUZZLE_TYPES)
                grid[y][x] = Puzzle(x, y, puzzle_type)

これにより、常に盤面がパズルで埋まった状態になり、ゲームを続けることができるようになっています。

3.5. 画面描画の仕組み

これらのパズルの状態を画面に表示するのが draw_grid() 関数です。

def draw_grid(surface, grid):
    for row in grid:
        for puzzle in row:
            if puzzle:
                puzzle.draw(surface)

この関数は、盤面の各マスに存在するパズル(puzzle オブジェクト)の draw() メソッドを呼び出すことで、それぞれのパズルを描画しています。Puzzle クラスの draw() メソッドでは、パズルの色と形(ここでは四角形)を描画する処理が記述されています。

class Puzzle:
    # ... (省略) ...
    def draw(self, surface):
        pygame.draw.rect(surface, self.color, self.rect)
        # 中央に種類を表すテキストを描画 (デバッグ用)
        font = pygame.font.Font(None, 30)
        text = font.render(self.type[:1], True, (255, 255, 255))
        text_rect = text.get_rect(center=self.rect.center)
        surface.blit(text, text_rect)

ここでは、パズルの色 (self.color) で塗りつぶされた四角形 (self.rect) を描画しています。また、デバッグ用にパズルの種類を表す最初の1文字を中央に表示するようにしています。

4. 開発に使用した技術:PythonとPygame

このゲームは、プログラミング言語の Python と、ゲーム開発のためのライブラリである Pygame を使って作られています。

4.1. なぜPythonを選んだのか?

Pythonは、初心者にも非常に学びやすいプログラミング言語の一つです。コードがシンプルで読みやすく、様々な用途に使える汎用性の高さが魅力です。ゲーム開発だけでなく、Webサイト作成やデータ分析、AI(人工知能)の開発など、幅広い分野で活用されています。

豊富なライブラリが揃っているのもPythonの強みで、ゲーム開発においては Pygame のような強力なツールを利用することができます。

4.2. Pygameとは?ゲーム開発の強力な味方

ライブラリ とは、特定の機能を実現するためによく使われるプログラムの集まりのことです。Pygame は、Pythonでゲームを作るために必要な機能(画面の描画、キーボードやマウスの入力処理、音の再生など)をたくさん含んでいます。

Pygame を使うことで、ゲーム開発者はこれらの基本的な機能を自分で一から作る必要がなくなり、ゲームのアイデアを形にすることに集中できるのです。今回のパズドラ風ゲームでも、Pygame の機能を使って、画面の表示やマウス操作の処理などを簡単に行うことができました。

5. 主要なコードの解説

ここでは、ゲームの動きを理解する上で特に重要なコードの断片を見ていきましょう。

5.1. ゲームのメインループ (main.py)

main.py は、ゲーム全体の流れを制御する中心的なファイルです。ゲームの起動、イベントの処理(マウスのクリックや移動など)、画面の更新といった、ゲームの基本的な動作は、このファイルの中にある メインループ (while running:) で行われています。

if __name__ == '__main__':
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("パズドラ風ゲーム")

    grid = create_grid()
    dragging_puzzle = None
    initial_grid_pos = None
    drag_start_pos = None
    last_swap_time = 0
    SWAP_INTERVAL = 150 # ミリ秒
    FALL_SPEED = 30   # 落下速度 (調整可能)

    running = True
    while running:
        current_time = pygame.time.get_ticks()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                # ... (マウスボタンが押された時の処理) ...
            elif event.type == pygame.MOUSEBUTTONUP:
                # ... (マウスボタンが離された時の処理) ...
            elif event.type == pygame.MOUSEMOTION:
                # ... (マウスが動いた時の処理) ...

        # 落下中のパズルのアニメーションを更新
        for row in grid:
            for puzzle in row:
                if puzzle:
                    puzzle.update_fall(FALL_SPEED)

        screen.fill((0, 0, 0))
        draw_grid(screen, grid)
        if dragging_puzzle:
            dragging_puzzle.draw(screen)
        pygame.display.flip()

    pygame.quit()

このループは、ゲームが起動している間、常に繰り返されます。pygame.event.get() で発生した様々なイベント(ウィンドウを閉じる、マウスの操作など)をチェックし、それぞれのイベントに対応した処理を行います。

ループの中では、落下中のパズルのアニメーションを更新したり、盤面を描画したり、ドラッグ中のパズルを描画したりしています。そして、pygame.display.flip() で画面を更新し、最新の状態を表示しています。

5.2. パズルクラス (puzzle.py)

puzzle.py では、一つ一つのパズルを表現する Puzzle クラスが定義されています。このクラスは、パズルの位置、種類、色、そして落下に関する情報を保持し、描画や移動、落下といったパズルの基本的な動作を定義しています。

Python

class Puzzle:
    def __init__(self, x, y, type):
        self.x = x
        self.y = y
        self.type = type
        self.color = COLORS[type]
        self.rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
        self.is_falling = False   # 落下中かどうかを示すフラグ
        self.fall_start_y = 0     # 落下開始時のY座標
        self.fall_distance = 0   # 現在の落下距離

    def start_fall(self, start_y):
        """落下アニメーションを開始する"""
        self.is_falling = True
        self.fall_start_y = start_y
        self.fall_distance = 0

    def update_fall(self, speed):
        """落下アニメーションを更新する"""
        if self.is_falling:
            self.fall_distance += speed
            self.rect.y = self.fall_start_y + int(self.fall_distance)
            if self.rect.y >= self.y * CELL_SIZE:
                self.rect.y = self.y * CELL_SIZE
                self.is_falling = False
                self.fall_start_y = 0
                self.fall_distance = 0

    def draw(self, surface):
        pygame.draw.rect(surface, self.color, self.rect)
        # 中央に種類を表すテキストを描画 (デバッグ用)
        font = pygame.font.Font(None, 30)
        text = font.render(self.type[:1], True, (255, 255, 255))
        text_rect = text.get_rect(center=self.rect.center)
        surface.blit(text, text_rect)

    def move(self, dx, dy):
        """パズルの位置を相対的に移動する"""
        self.x += dx
        self.y += dy
        self.rect.x = self.x * CELL_SIZE
        self.rect.y = self.y * CELL_SIZE

__init__ メソッドは、パズルが生成されるときに呼び出され、初期位置や種類、色などを設定します。update_fall() メソッドは、落下中のパズルの位置を少しずつ更新することで、アニメーションを実現しています。

5.3. グリッド操作関連の関数 (puzzle.py)

puzzle.py には、盤面(グリッド)の状態を操作するための様々な関数が定義されています。create_grid()draw_grid()swap_grid_positions()get_puzzle_at_pos()get_grid_pos_from_mouse()find_matches()find_matches_after_move()clear_matches()drop_puzzles()generate_new_puzzles() などが、それぞれの役割を担っています。

これらの関数が連携することで、パズルの生成から移動、消去、落下、そして新しいパズルの生成という一連の流れがスムーズに行われています。

6. 遊んでみよう!ゲームの操作方法

このゲームを実際に遊ぶには、以下の手順が必要です。

  1. Pythonのインストール: まず、お使いのパソコンに Python がインストールされている必要があります。もしインストールされていない場合は、Pythonの公式サイトからダウンロードしてインストールしてください。
  2. Pygameのインストール: コマンドプロンプトやターミナルを開き、「pip install pygame」と入力して Pygame をインストールします。 pip は、Pythonのパッケージ(ライブラリなどのまとまり)を管理するためのツールです。
  3. コードの保存: 上記の main.pypuzzle.py のコードを、同じフォルダ内に保存します。
  4. ゲームの実行: コマンドプロンプトやターミナルで、保存したフォルダに移動し、「python main.py」と入力して実行します。

ゲームが起動したら、マウスを使ってパズルをドラッグし、同じ色のパズルを縦や横に3つ以上並べてみてください。パズルが消えて、新しいパズルが落ちてくる様子を楽しめるはずです。

7. 今後の展望と更なる進化

今回の完成版は、パズドラ風ゲームの基本的な部分を実装したに過ぎません。今後は、さらに面白いゲームにするために、様々な機能を追加していきたいと考えています。

  • スコア表示: パズルを消した数や連鎖数に応じてスコアを表示する機能を追加したいです。
  • 時間制限: ゲームに緊張感を持たせるために、制限時間を設けたいと考えています。
  • 特別なパズルやスキル: 特定の条件で出現する特別なパズルや、それらを使ったスキルを追加することで、戦略性を高めたいです。
  • グラフィックの向上: パズルのデザインや背景をもっと魅力的なものにしたいです。
  • サウンドエフェクトやBGM: ゲームの臨場感を高めるために、効果音やBGMを追加したいです。

これらの機能を追加していくことで、より本格的なパズドラ風ゲームに近づけていければと思っています。

8. まとめ:ゲーム開発は夢を形にする力

いかがでしたでしょうか?

今回は、自作のパズドラ風ゲームの完成報告と、その開発の道のり、そして遊び方について解説しました。

ゲーム開発は決して簡単な道のりではありませんでしたが、少しずつ機能を実装していく中で、自分のアイデアが形になっていく喜びは何物にも代えがたいものでした。

もしあなたが「自分もゲームを作ってみたい」と思っているなら、まずは簡単なゲームから挑戦してみることをお勧めします。今回ご紹介した Python と Pygame は、初心者でも比較的扱いやすく、ゲーム開発の入門には最適な組み合わせです。

このゲームが、皆さんのプログラミング学習やゲーム開発への興味を持つきっかけになれば、とても嬉しく思います。

最後まで読んでいただき、ありがとうございました!これからも、ゲーム開発の進捗をブログで報告していきたいと思いますので、ぜひ応援してくださいね!

コメント

PAGE TOP
タイトルとURLをコピーしました