読者です 読者をやめる 読者になる 読者になる

人工知能に関する断創録

人工知能、認知科学、心理学、ロボティクス、生物学などに興味を持っています。このブログでは人工知能のさまざまな分野について調査したことをまとめています。最近は、機械学習・Deep Learningに関する記事が多いです。



ボールの反射音

Pygame

ボールがブロックやパドルに当たったとき音が鳴るように改良します。効果音はザ・マッチメイカァズさんからお借りしています。

breakout04.zip
f:id:aidiary:20100807231807p:plain

効果音のロード

まず効果音をロードします。効果音はすべてBallのクラス変数へセットします。クラス変数なのでボールのオブジェクトをたくさん作ってもサウンドのロードは1回で済みます。サウンドのようにすべてのボールに共通した属性はクラス変数とした方がいいですね。

今、気がつきましたがボールやブロックの画像もクラス変数にした方がよかったです。画像のロードが1回で済みますね。今はインスタンス変数にしているのでオブジェクトを作成するたびに画像をファイルからロードしてしまいます。

    # サウンドのロード
    Ball.paddle_sound = load_sound("wood00.wav")  # パドルとの衝突音
    Ball.brick_sound = load_sound("chari06.wav")  # ブロックとの衝突音
    Ball.fall_sound = load_sound("fall06.wav")    # ボールを落とした音

サウンドのロードはmain()で1回だけ実行します。ボールがパドルと衝突したときの音はBall.paddle_sound、ボールがブロックと衝突したときの音はBall.brick_sound、ボールをパドルで受け損ねて落としたときの音はBall.fall_soundにロードします。 Ballは特定のオブジェクト名ではなくクラス名なので上の変数はすべてクラス変数となりすべてのボールオブジェクトで共有されます。

上のコードでpaddle_soundとかの変数がBallクラスで定義されてないのに代入してよいのか?疑問に思った方もいるかもしれません。C++やJavaのような言語ではこのようなマネはできないのですがPythonではOKです。Pythonでは、インスタンス変数やクラス変数は代入すると同時に自動的に作られます。だから上のような書き方はOKです。とはいうものの代入する前に変数にアクセスすることはできません。ここら辺はプログラマがしっかり管理してないとダメですね。

def load_sound(filename):
    filename = os.path.join("data", filename)
    return pygame.mixer.Sound(filename)

load_sound()は、効果音をロードしてサウンドオブジェクトを返す関数です。効果音のロードと再生は非常に簡単ですが、基礎編の効果音のロードと再生(2008/5/11)でも解説しました。

効果音の再生

効果音がロードできたので適切な場所で鳴るようにします。すべてBallのmove()で鳴らしています。

    def move(self):
        """ボールの移動"""
        self.rect.centerx += self.dx
        self.rect.centery += self.dy
        # 壁との反射
        if self.rect.left < SCR_RECT.left:  # 左側
            self.rect.left = SCR_RECT.left
            self.dx = -self.dx  # 速度を反転
        if self.rect.right > SCR_RECT.right:  # 右側
            self.rect.right = SCR_RECT.right
            self.dx = -self.dx
        if self.rect.top < SCR_RECT.top:  # 上側
            self.rect.top = SCR_RECT.top
            self.dy = -self.dy
        # パドルとの反射
        if self.rect.colliderect(self.paddle.rect) and self.dy > 0:
            self.dy = -self.dy
            self.paddle_sound.play()
        # ボールを落とした場合
        if self.rect.top > SCR_RECT.bottom:
            self.update = self.start  # ボールを初期状態に
            self.fall_sound.play()
        # ブロックを壊す
        # ボールと衝突したブロックリストを取得
        bricks_collided = pygame.sprite.spritecollide(self, self.bricks, True)
        if bricks_collided:  # 衝突ブロックがある場合
            oldrect = self.rect
            for brick in bricks_collided:  # 各衝突ブロックに対して
                # ボールが左から衝突
                if oldrect.left < brick.rect.left < oldrect.right < brick.rect.right:
                    self.rect.right = brick.rect.left
                    self.dx = -self.dx
                # ボールが右から衝突
                if brick.rect.left < oldrect.left < brick.rect.right < oldrect.right:
                    self.rect.left = brick.rect.right
                    self.dx = -self.dx
                # ボールが上から衝突
                if oldrect.top < brick.rect.top < oldrect.bottom < brick.rect.bottom:
                    self.rect.bottom = brick.rect.top
                    self.dy = -self.dy
                # ボールが下から衝突
                if brick.rect.top < oldrect.top < brick.rect.bottom < oldrect.bottom:
                    self.rect.top = brick.rect.bottom
                    self.dy = -self.dy
                self.brick_sound.play()

パドルと反射したとき、ボールを落としたとき、ブロックを壊したときに効果音をplay()で鳴らしています。クラス変数にもselfは必要です。壁と当たったときにも鳴らそうと思いましたが、うるさかったので止めました。