【Pygame】ブロック崩しの作り方(効果音付き)

Pygameを用いてブロック崩しを作る方法とソースコードを解説します。

スポンサーリンク

ブロック崩しを作る

前回までの記事では、Python+Pygameでブロック崩しの処理の一部を作っていきました。
今回はそれらを合わせて、効果音もつけてブロック崩しゲームの完成版を作ってみました。

動画解説

本ページの内容は以下動画でも解説しています。

前回までの記事はこちら
1 【Python/Pygame】ブロック崩しのパドル(バー)の作成
2 【Python/Pygame】ブロック崩しのブロックの作成
3 【Python/Pygame】ブロック崩しのボールの作成
4 【Python/Pygame】ブロック崩しの得点の計算・表示
スポンサーリンク

処理手順

プログラムの処理の流れです。

説明
「pygame」「math」「sys」モジュールをインポートする。
画面のサイズを設定する。(400*400)
Pygameを初期化する。[pygame.init]
画面(ウィンドウ)を生成する。[pygame.display.set_mode]
効果音を読み込む。(バドルの反射音など)
バドルを作成する。
ブロックを作成する。(14*10個)
スコア表示を作成する。
ボールを作成する。
画面のフレームレートを設定する。
スプライトを背景画像で消去する。
スプライトグループを更新する。(group.update)
スプライトグループを描画する。(group.draw)
画面を更新する。
イベント処理をする。
画面の閉じるボタンが押されたら終了する。
⑩~⑯の処理を繰り返す。
ゲームの構成素材
paddle.png
ball.png
block.png
効果音 flashing.wav、flying_pan.wav、badend1.wav
※下記サイト様から「flashing.mp3」、「flying_pan.mp3」、「badend1.mp3」をお借りし、Wav形式に変換しました。
http://taira-komori.jpn.org/anime01.html
スポンサーリンク

ソースコード

サンプルプログラムのソースコードです。

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import math
import sys
import pygame.mixer

# 画面サイズ
SCREEN = Rect(0, 0, 400, 400)

# バドルのクラス
class Paddle(pygame.sprite.Sprite):
    # コンストラクタ(初期化メソッド)
    def __init__(self, filename):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.image.load(filename).convert()
        self.rect = self.image.get_rect()
        self.rect.bottom = SCREEN.bottom - 20          # パドルのy座標

    def update(self):
        self.rect.centerx = pygame.mouse.get_pos()[0]  # マウスのx座標をパドルのx座標に
        self.rect.clamp_ip(SCREEN)                     # ゲーム画面内のみで移動

# ボールのクラス
class Ball(pygame.sprite.Sprite):
    # コンストラクタ(初期化メソッド)
    def __init__(self, filename, paddle, blocks, score, speed, angle_left, angle_right):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.image.load(filename).convert()
        self.rect = self.image.get_rect()
        self.dx = self.dy = 0  # ボールの速度
        self.paddle = paddle  # パドルへの参照
        self.blocks = blocks  # ブロックグループへの参照
        self.update = self.start # ゲーム開始状態に更新
        self.score = score
        self.hit = 0  # 連続でブロックを壊した回数
        self.speed = speed # ボールの初期速度
        self.angle_left = angle_left # パドルの反射方向(左端:135度)
        self.angle_right = angle_right # パドルの反射方向(右端:45度)

    # ゲーム開始状態(マウスを左クリック時するとボール射出)
    def start(self):
        # ボールの初期位置(パドルの上)
        self.rect.centerx = self.paddle.rect.centerx
        self.rect.bottom = self.paddle.rect.top

        # 左クリックでボール射出
        if pygame.mouse.get_pressed()[0] == 1:
            self.dx = 0
            self.dy = -self.speed
            self.update = self.move

    # ボールの挙動
    def move(self):
        self.rect.centerx += self.dx
        self.rect.centery += self.dy

        # 壁との反射
        if self.rect.left < SCREEN.left:    # 左側
            self.rect.left = SCREEN.left
            self.dx = -self.dx              # 速度を反転
        if self.rect.right > SCREEN.right:  # 右側
            self.rect.right = SCREEN.right
            self.dx = -self.dx
        if self.rect.top < SCREEN.top:      # 上側
            self.rect.top = SCREEN.top
            self.dy = -self.dy

        # パドルとの反射(左端:135度方向, 右端:45度方向, それ以外:線形補間)
        # 2つのspriteが接触しているかどうかの判定
        if self.rect.colliderect(self.paddle.rect) and self.dy > 0:
            self.hit = 0                                # 連続ヒットを0に戻す
            (x1, y1) = (self.paddle.rect.left - self.rect.width, self.angle_left)
            (x2, y2) = (self.paddle.rect.right, self.angle_right)
            x = self.rect.left                          # ボールが当たった位置
            y = (float(y2-y1)/(x2-x1)) * (x - x1) + y1  # 線形補間
            angle = math.radians(y)                     # 反射角度
            self.dx = self.speed * math.cos(angle)
            self.dy = -self.speed * math.sin(angle)
            self.paddle_sound.play()                    # 反射音

        # ボールを落とした場合
        if self.rect.top > SCREEN.bottom:
            self.update = self.start                    # ボールを初期状態に
            self.gameover_sound.play()
            self.hit = 0
            self.score.add_score(-100)                  # スコア減点-100点

        # ボールと衝突したブロックリストを取得(Groupが格納しているSprite中から、指定したSpriteと接触しているものを探索)
        blocks_collided = pygame.sprite.spritecollide(self, self.blocks, True)
        if blocks_collided:  # 衝突ブロックがある場合
            oldrect = self.rect
            for block in blocks_collided:
                # ボールが左からブロックへ衝突した場合
                if oldrect.left < block.rect.left and oldrect.right < block.rect.right:
                    self.rect.right = block.rect.left
                    self.dx = -self.dx

                # ボールが右からブロックへ衝突した場合
                if block.rect.left < oldrect.left and block.rect.right < oldrect.right:
                    self.rect.left = block.rect.right
                    self.dx = -self.dx

                # ボールが上からブロックへ衝突した場合
                if oldrect.top < block.rect.top and oldrect.bottom < block.rect.bottom:
                    self.rect.bottom = block.rect.top
                    self.dy = -self.dy

                # ボールが下からブロックへ衝突した場合
                if block.rect.top < oldrect.top and block.rect.bottom < oldrect.bottom:
                    self.rect.top = block.rect.bottom
                    self.dy = -self.dy
                self.block_sound.play()     # 効果音を鳴らす
                self.hit += 1               # 衝突回数
                self.score.add_score(self.hit * 10)   # 衝突回数に応じてスコア加点

# ブロックのクラス
class Block(pygame.sprite.Sprite):
    def __init__(self, filename, x, y):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.image.load(filename).convert()
        self.rect = self.image.get_rect()
        # ブロックの左上座標
        self.rect.left = SCREEN.left + x * self.rect.width
        self.rect.top = SCREEN.top + y * self.rect.height

# スコアのクラス
class Score():
    def __init__(self, x, y):
        self.sysfont = pygame.font.SysFont(None, 20)
        self.score = 0
        (self.x, self.y) = (x, y)
    def draw(self, screen):
        img = self.sysfont.render("SCORE:"+str(self.score), True, (255,255,250))
        screen.blit(img, (self.x, self.y))
    def add_score(self, x):
        self.score += x

def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN.size)
    Ball.paddle_sound = pygame.mixer.Sound(
        "C:/github/sample/python/pygame/breakout/flashing.wav")    # パドルにボールが衝突した時の効果音取得
    Ball.block_sound = pygame.mixer.Sound(
        "C:/github/sample/python/pygame/breakout/flying_pan.wav")    # ブロックにボールが衝突した時の効果音取得
    Ball.gameover_sound = pygame.mixer.Sound(
        "C:/github/sample/python/pygame/breakout/badend1.wav")    # ゲームオーバー時の効果音取得
    # 描画用のスプライトグループ
    group = pygame.sprite.RenderUpdates()

    # 衝突判定用のスプライトグループ
    blocks = pygame.sprite.Group()

    # スプライトグループに追加
    Paddle.containers = group
    Ball.containers = group
    Block.containers = group, blocks

    # パドルの作成
    paddle = Paddle("C:/github/sample/python/pygame/breakout/paddle.png")

    # ブロックの作成(14*10)
    for x in range(1, 15):
        for y in range(1, 11):
            Block("C:/github/sample/python/pygame/breakout/block.png", x, y)

    # スコアを画面(10, 10)に表示
    score = Score(10, 10)

    # ボールを作成
    Ball("C:/github/sample/python/pygame/breakout/ball.png",
         paddle, blocks, score, 5, 135, 45)

    clock = pygame.time.Clock()

    while (1):
        clock.tick(60)      # フレームレート(60fps)
        screen.fill((0,20,0))
        # 全てのスプライトグループを更新
        group.update()
        # 全てのスプライトグループを描画
        group.draw(screen)
        # スコアを描画
        score.draw(screen)
        # 画面更新
        pygame.display.update()

        # キーイベント(終了)
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()

if __name__ == "__main__":
    main()

Pygameの使い方・サンプルゲームまとめ
Pythonモジュール「Pygame」で2Dゲームを簡単に制作する方法を解説します。
【Python超入門】使い方とサンプル集
Pythonとは、統計処理や機械学習、ディープラーニングといった数値計算分野を中心に幅広い用途で利用されている人気なプログラミング言語です。 主な特徴として「効率のよい、短くて読みやすいコードを書きやすい」、「ライブラリが豊富なのでサクッと...
Python
スポンサーリンク
西住工房

コメント

  1. たすけてーー より:

    # 壁との反射
    if self.rect.left SCREEN.right: # 右側

    ここの部分にエラーが出てしまいます原因は何でしょうか?

  2. あああ より:

    # 壁との反射
    if self.rect.left SCREEN.right: # 右側

    ここの部分にエラーが出てしまいます。
    まだまだ初心者でとりあえずコピー&ペーストで実行しているだけなのですが、困っています。
    = がないのが原因でしょうか?

    よろしくお願いします。

    • 管理人 より:

      コメントありがとうございます。
      ご指摘のとおり等号、不等号がないのが原因です。
      記事のソースコードを修正しましたのでご確認くさだい。

  3. Tetsu より:

    使用しているpngはどこで入手できますか?
    URLとかあれば教えてください。

    • 管理人 より:

      画像ファイル上で右クリックし、[画像ファイルを保存する]などを選択すると元画像を保存できます。

  4. わからない太郎 より:

    参考にさせていただいております。

    どうしても、ソースコードを入れ込んでみたのですが、importの箇所で読み込みません。

    完成品と比較をしたいのでファイルをいただければ幸いです。
    どこができていないのか比較をしたいです。
    何卒よろしくお願いいたします。

    • 管理人 より:

      掲載しているプログラムが完成版です。
      ゲーム内でロードする画像ファイルは既に掲載しておりますが、音声ファイルは再配布禁止のフリー音源を使用しているため
      入手先を紹介するまでとなっております。
      そのため、全ファイル一式をご提供というのはできませんので、ご理解いただけますと幸いです。
      importでエラーが出ているということはpygameモジュールをインストールできていない可能性がありますので
      再度、「pip install pygame」でインストールされてみてはいかがでしょうか。

  5. 匿名 より:

    LinuxでPythonを勉強し始めた素人です。
    ファイルの部分で若干修正を要しましたが、無事動きました。

    ありがとうございます。