【Pygame】ブロック崩しのボール作成

この記事では、Pygameを用いてブロック崩しのボールを作る方法とソースコードを解説します。

スポンサーリンク

ブロック崩しのブロック

今回はPygameでパドル・ブロックに加えて、ブロック崩しのボールを作ってみました。

スポンサーリンク

処理手順

プログラムの処理の流れは下記の通りです。

説明
「pygame」「math」「sys」モジュールをインポートする。
画面のサイズを設定する。(400*400)
Pygameを初期化する。[pygame.init]
画面(ウィンドウ)を生成する。[pygame.display.set_mode]
描画用のスプライトグループを作成する。
パドル・ブロック・ボールを作成する。
画面のフレームレートを設定する。
スプライトグループを更新する。(group.update)
スプライトグループを描画する。(group.draw)
画面を更新する。
イベント処理をする。
・画面の閉じるボタンが押されたら終了する。
・Escキーが押されたら終了する。
10-11の処理を繰り返す。
ファイル名 使用した素材
paddle.png
ball.png
block.png
スポンサーリンク

ソースコード

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

#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):
    speed = 5
    angle_left = 135
    angle_right = 45

    def __init__(self, filename, paddle, blocks):
        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.hit = 0  # 連続でブロックを壊した回数

    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度方向, それ以外:線形補間)
        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)
        # ボールを落とした場合
        if self.rect.top > SCREEN.bottom:
            self.update = self.start                    # ボールを初期状態に
            self.hit = 0

        # ボールと衝突したブロックリストを取得
        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 < oldrect.right < block.rect.right:
                    self.rect.right = block.rect.left
                    self.dx = -self.dx
                # ボールが右から衝突
                if block.rect.left < oldrect.left < block.rect.right < oldrect.right:
                    self.rect.left = block.rect.right
                    self.dx = -self.dx
                # ボールが上から衝突
                if oldrect.top < block.rect.top < oldrect.bottom < block.rect.bottom:
                    self.rect.bottom = block.rect.top
                    self.dy = -self.dy
                # ボールが下から衝突
                if block.rect.top < oldrect.top < block.rect.bottom < oldrect.bottom:
                    self.rect.top = block.rect.bottom
                    self.dy = -self.dy
                self.hit += 1               # 衝突回数

# ブロックのクラス
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

def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN.size)
    group = pygame.sprite.RenderUpdates()  # 描画用のスプライトグループ
    blocks = pygame.sprite.Group()       # 衝突判定用のスプライトグループ
    Paddle.containers = group
    Ball.containers = group
    Block.containers = group, blocks
    paddle = Paddle("paddle.png")           # パドルの作成
    # ブロックの作成(14*10)
    for x in range(1, 15):
        for y in range(1, 11):
            Block("block.png", x, y)

    Ball("ball.png", paddle, blocks) # ボールを作成
    clock = pygame.time.Clock()

    while (1):
        clock.tick(60)      # フレームレート(60fps)
        screen.fill((0,20,0))
        group.update()        # 全てのスプライトグループを更新
        group.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()
スポンサーリンク

実行結果

サンプルプログラムの実行結果です。

- おすすめ関連記事
1 Pygameでブロック崩し(完成版)
2 Pygameでゲームプログラミング
3 Python入門 基本文法
Python
スポンサーリンク

コメント

  1. @dqx_psyc より:

    ここのサイトでpythonの勉強をさせていただいてます。
    ありがとうございます。
    質問は的外れなのかもしれませが、知っていたら教えていただけないでしょうか?
    下記はspriteクラスの抜粋なのですが、addメソッドのforループ内の最後elseの場合にaddメソッドを再帰的に呼び出しているのですが、こうするとelseが発生すると永遠にaddメソッドがループするような気がするのですが解釈が間違っているのでしょうか?

    class Sprite(object):

    def __init__(self, *groups):
    self.__g = {} # The groups the sprite is in
    if groups:
    self.add(*groups)

    def add(self, *groups):
    has = self.__g.__contains__
    for group in groups:
    if hasattr(group, ‘_spritegroup’):
    if not has(group):
    group.add_internal(self)
    self.add_internal(group)
    else:
    self.add(*group)

    • 管理人 より:

      ※@dqx_psyc様
      コメントありがとうございます。
      私も無限ループする気がします。
      else文にprint関数などを入れて
      永遠にループしているか確かめられてみてはどうでしょうか。