人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

スコアの表示

ゲームスコアが画面の中央に表示されるようにします。ブロックを連続して壊すことで高得点が狙えるようにしました。ボールを落としてしまうと減点です。

breakout06.zip
f:id:aidiary:20100807232541p:plain

スコアの表示

まず、スコアボードを表すクラスScoreBoardを作ります。

class ScoreBoard():
    """スコアボード"""
    def __init__(self):
        self.sysfont = pygame.font.SysFont(None, 80)
        self.score = 0
    def draw(self, screen):
        score_img = self.sysfont.render(str(self.score), True, (255,255,0))
        x = (SCR_RECT.size[0] - score_img.get_width()) / 2
        y = (SCR_RECT.size[1] - score_img.get_height()) / 2
        screen.blit(score_img, (x, y))
    def add_score(self, x):
        self.score += x

フォントは画像で用意したいところですが、システムフォントをそのまま使っています。見た目がちょっと味気ないですけど。add_score()の引数に点数を指定すると得点が増えます。得点の描画はdraw()です。スコアが画面の中央に表示される座標 (x,y) を計算して、スコアを描画しています。テキストを描画する方法は、テキストを描画する(2008/5/4)で書きました。

得点の計算

次にスコアボードの得点を加える処理です。これは、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.hit = 0  # 連続ヒットを0に戻す
            # パドルの左端に当たったとき135度方向、右端で45度方向とし、
            # その間は線形補間で反射方向を計算
            x1 = self.paddle.rect.left - self.rect.width  # ボールが当たる左端
            y1 = self.angle_left  # 左端での反射方向(135度)
            x2 = self.paddle.rect.right  # ボールが当たる右端
            y2 = self.angle_right  # 右端での反射方向(45度)
            m = float(y2-y1) / (x2-x1)  # 直線の傾き
            x = self.rect.left  # ボールが当たった位置
            y = m * (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 > SCR_RECT.bottom:
            self.update = self.start  # ボールを初期状態に
            self.fall_sound.play()
            # ボールを落としたら-30点
            self.hit = 0
            self.score_board.add_score(-30)
        # ブロックを壊す
        # ボールと衝突したブロックリストを取得
        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()
                # 点数を追加
                self.hit += 1
                self.score_board.add_score(self.hit * 10)

ポイントは、hitという変数です。これは、ブロックを連続して壊すと+1されていきます。もし、ボールを落としたり、パドルに当てたら0に戻ってしまいます。得点は、一番最後の行にあるようにhit*10点を追加しています。つまり、ブロックをパドルで受けずに連続してブロックを壊せれば高得点が入ります。ボールを落としてしまったら-30点です。ブロックの配置で上下左右にスペースを空けていますが、ボールをこの間に潜り込ませられれば大量のブロックを連続して壊せて高得点が狙えます。ちょっと難しいけど。スコアが出せたので次はランキングを登録できたりするとおもしろそうです。Pythonはゲームだけでなく、CGIも書けるので簡単にランキングのページを作れます。そのうち取り上げます。