Python プログラミング教室

2025年10月25日の実践コースはPythonでスイカゲームに挑戦

今回から新しいゲームに挑戦します。Scratchで諦めたシリーズということで今回はス〇カゲームを作ってみたいと思います。

スタート地点のコードはPygameでゲームを作るなら毎回使えそうな共通的な部分から始めます。まずは以下のコードをコピーして実行してみよう!ファイル名の最後は「.py」にすることをお忘れなく!

import sys                                # 標準ライブラリ:終了処理などに使用
import pygame                             # Pygame:描画と入力

W, H = 480, 720                           # 画面サイズ(幅・高さ)
FPS = 60                                  # フレームレート
DROP_MIN_X = 60                           # 落下可能な最小X(左の壁から少し内側)
DROP_MAX_X = W - 60                       # 落下可能な最大X(右の壁から少し内側)
DROP_Y = 60                               # 投入(落下開始)高さ
TOP_LINE_Y = 100                          # 天井ライン(ゲームオーバー判定用の目安)

# ------------------------------------------------------------
# Player:ユーザが操作できるオブジェクト
# ------------------------------------------------------------
class Player:
    def __init__(self):
        self.aim_x = (DROP_MIN_X + DROP_MAX_X) // 2         # 照準Xの初期位置(中央寄り)
        self.aim_y = DROP_Y                                 # 照準Yは固定(投入高さ)
        self.line_top_y = 0                                 # 照準縦線の開始Y
        self.line_bottom_y = 90                             # 照準縦線の終了Y(見た目用)
        self.line_width = 6                                 # 縦線の太さ
        self.dot_radius = 10                                # ドット(照準点)の半径
        self.color = (140,160,240)                          # 照準の色(淡いブルー)

    def update_from_mouse(self):
        mx, my = pygame.mouse.get_pos()                     # 現在のマウスのX座標を取得 ※Y座標も取得しているが未使用
        self.aim_x = mx                                     # TODO: マウスXを範囲内に制限して採用

    def draw_aim(self, screen: pygame.Surface):
        pygame.draw.line(screen, self.color, (self.aim_x, self.line_top_y), (self.aim_x, self.line_bottom_y), self.line_width) # 上部の狙いガイド縦線

# ------------------------------------------------------------
# 見た目担当:壁・床・天井ライン・ヒント表示
# ------------------------------------------------------------
class WorldView:
    def __init__(self, screen: pygame.Surface):
        self.screen = screen                                 # 描画先Surfaceを保持

    def draw_base(self):
        # TODO: 背景を描く
        pass

# ------------------------------------------------------------
# ゲーム全体
# ------------------------------------------------------------
class Game:
    def __init__(self):
        pygame.init()                                        # Pygame初期化
        self.screen = pygame.display.set_mode((W, H))        # ウィンドウ生成
        pygame.display.set_caption("Suika Step 1")           # タイトル設定
        self.clock = pygame.time.Clock()                     # 時間管理(FPS制御)
        self.worldView = WorldView(self.screen)              # 見た目管理を構築
        self.player = Player()                               # ユーザ操作クラス(Aim)

    def handle_events(self) -> bool:
        for e in pygame.event.get():                         # イベントループ開始
            if e.type == pygame.QUIT:                        # 閉じるボタンが押されたら
                return False                                 # ループ終了を指示
            if e.type == pygame.KEYDOWN:                     # キーボードが押されたら
                if e.key == pygame.K_ESCAPE:                 # ESCキーなら
                    return False                             # ループ終了を指示
                if e.key == pygame.K_SPACE:                  # スペースキーが押された
                    pass                                     # TODO: 後で「果物を落とす」を実装
        return True                                          # 続行する

    def update(self, dt: float):
        self.player.update_from_mouse()                      # マウス位置からAimのXを更新

    def draw(self):
        self.worldView.draw_base()                           # 背景・壁・天井ラインを描画
        self.player.draw_aim(self.screen)                    # Aim(照準)を描画
        pygame.display.flip()                                # 画面を更新(ダブルバッファ反映)

    def run(self):
        running = True                                       # メインループの継続フラグ
        while running:                                       # メインループ開始
            dt = self.clock.tick(FPS) / 1000.0               # 経過時間(秒)を取得
            running = self.handle_events()                   # 入力イベント処理(継続可否)
            self.update(dt)                                  # 状態更新(Aim/物理)
            self.draw()                                      # 描画
        pygame.quit()                                        # Pygameの終了処理
        sys.exit()                                           # プロセスを終了

if __name__ == "__main__":
    Game().run()                                             # ゲームを起動

ステップ1 背景とプレイヤーを描く

ステップ1-0:初めのコード

まずは初めのコードを実行しよう

初めのコードを実行したらどうなったでしょうか?変な線が表示されてどんどん画面が汚くなっていったでしょうか?
これは、画面をキレイにする処理が入っていないためです。まずは画面をキレイにするプログラムを追加しましょう。

次は初めのコードを読んでいこう

この講座では何度かプログラミングの上手な読み方をお伝えしています。
1行1行読むのではなく、全体像を把握するように塊ごとにどんなことをしているのかを大雑把に読んでいきます。

ある程度理解できたら少しずつプログラムを修正していきましょう。

ステップ1-1:背景色を塗ることで動きを表現

以下の1行を追加すると毎回背景がきれいになって、プレイヤーが動いているように見えるようになります。
どこに追加したらいいのかは各自で考えてみてください。

追加コード:

        self.screen.fill((245, 245, 250))                    # 背景色を塗る(薄いグレー)

ステップ1-2:背景の枠を描く

床のライン

以下のコードを正しいところに追加すると、画面上に床のラインが表示されます。
床のラインは画面の下から40pxのところに描かれます。

        pygame.draw.line(self.screen, (200,200,210), (40, H-40), (W-40, H-40), 4)       # 床ライン(グレー・太線)

左右の壁のライン

床のラインのコードをまねして左端から40pxのところに縦のラインを描いてみよう。

それができたら次は右端から40pxのところに縦のラインを描いてみよう。

ヒント:画面の左上が(0, 0)の座標になります。「W」と「H」を使ってうまく縦のラインを探してみよう。

天井ライン

ここまで出来たらゲームオーバーになる上限のラインを描きます。以下のコードを追加しよう。

        pygame.draw.line(self.screen, (220, 120, 120), (40, TOP_LINE_Y), (W - 40, TOP_LINE_Y), 2) # 天井ライン(赤系・細線)

ステップ1-3:プレイヤーが左右の壁を越えてしまう問題を修正

左右の端から60px分はプレイヤーが移動しないようにしたい。どうすればいいでしょうか?以下の変数を使って何とかプログラムを考えてみよう!

プレイヤーが移動できる左端のX座標は「DROP_MIN_X」です。
プレイヤーが移動できる右端のX座標は「DROP_MAX_X」です。

ステップ1-4:今後は何をしてけばいいか考えよう

次に何を追加したいのかは各自で考えて意見を出してみよう。
今回出た意見に合わせて次回追加する内容を考えたいと思います。

-Python, プログラミング教室