人工知能に関する断創録

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

エイリアンの反撃

エイリアンがビームを撃つようにします。

invader05.zip
f:id:aidiary:20100807235209p:plain

ビームクラス

エイリアンが発射するビームを実装します。まずShotクラスを全部コピーしてください。これで9割完成です(笑)プレイヤーが発射する弾とエイリアンが発射するビームはほとんど同じです。弾は上に飛んでいくのに対し、ビームは下に飛んでいくことだけが違います。なのでupdate()を少し変えるだけです。

class Beam(pygame.sprite.Sprite):
    """エイリアンが発射するビーム"""
    speed = 5  # 移動速度
    def __init__(self, pos):
        # imageとcontainersはmain()でセット
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.rect = self.image.get_rect()
        self.rect.center = pos
    def update(self):
        self.rect.move_ip(0, self.speed)  # 下へ移動
        if self.rect.bottom > SCR_RECT.height:  # 下端に達したら除去
            self.kill()

speedはShotより少し遅くしています。ビームは下へ移動するのでmove_ip()でself.speedという正の値を指定します。

エイリアンの攻撃

次にビームを使ってエイリアンが攻撃する処理です。Alienクラスのupdate()に追加します。

class Alien(pygame.sprite.Sprite):
    """エイリアン"""
    speed = 2  # 移動速度
    animcycle = 18  # アニメーション速度
    frame = 0
    move_width = 230  # 横方向の移動範囲
    prob_beam = 0.005  # ビームを発射する確率
    def __init__(self, pos):
        # imagesとcontainersはmain()でセット
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.rect.center = pos
        self.left = pos[0]  # 移動できる左端
        self.right = self.left + self.move_width  # 移動できる右端
    def update(self):
        # 横方向への移動
        self.rect.move_ip(self.speed, 0)
        if self.rect.center[0] < self.left or self.rect.center[0] > self.right:
            self.speed = -self.speed
        # ビームを発射
        if random.random() < self.prob_beam:
            Beam(self.rect.center)
        # キャラクターアニメーション
        self.frame += 1
        self.image = self.images[self.frame/self.animcycle%2]

prob_beamは、現在のフレームでビームを発射する確率です。この確率が高いとビームが出やすくなります。かなり小さめにしておかないとありえないくらい大量に出ます(笑)Pythonで乱数を扱うのがrandomモジュールです。random.random()で0以上1未満の乱数を返します。この乱数がprob_beamより小さかったらBeamオブジェクトを作ります。引数にはエイリアンの座標を与えます。こうしておくとエイリアンからビームが出たように見えます。Beamクラスはスプライトグループに登録してあるのでオブジェクトを作るだけで描画、更新されます。

プレイヤーとビームの衝突判定

最後にエイリアンの発射したビームとプレイヤーの衝突判定を実装します。この処理はエイリアンとミサイルの衝突判定とほとんど同じです。collision_detection()です。

def collision_detection(player, aliens, shots, beams):
    """衝突判定"""
    # エイリアンとミサイルの衝突判定
    alien_collided = pygame.sprite.groupcollide(aliens, shots, True, True)
    for alien in alien_collided.keys():
        Alien.kill_sound.play()
    # プレイヤーとビームの衝突判定
    beam_collided = pygame.sprite.spritecollide(player, beams, True)
    if beam_collided:  # プレイヤーと衝突したビームがあれば
        Player.bomb_sound.play()
        # TODO: ゲームオーバー処理

エイリアンとミサイルの衝突判定と違って、プレイヤースプライトはただ1つなのでpygame.sprite.groupcollide()ではなく、スプライトとスプライトグループの衝突を調べるpygame.sprite.spritecollide()を使います。

  pygame.sprite.spritecollide(sprite, group, dokill)

という形式の関数でspriteとgroup内のいずれかのスプライトが衝突したか調べ、group内の衝突したスプライトをリストで返します。 dokillをTrueにしておくとspriteと衝突したgroup内のスプライトをすべてgroupから消します。groupから消えたスプライトは draw()されなくなるので画面からも消えます。この関数はブロック崩し編でボールとブロックの衝突判定(2008/8/11)にも使いました。上の例では、プレイヤーと衝突したビームがあった場合に爆発音を鳴らしています。ただ、プレイヤーは無敵でまだゲームオーバーにはなりません。