人工知能に関する断創録

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

ミサイルの発射

スペースキーを押すと自機からミサイルが発射できるようにします。

invader02.zip
f:id:aidiary:20100807233803p:plain

ミサイルクラス

まず発射されるミサイルを表すクラスShotを作成します。これもスプライトで実装します。

class Shot(pygame.sprite.Sprite):
    """プレイヤーが発射するミサイル"""
    speed = 9  # 移動速度
    def __init__(self, pos):
        # imageとcontainersはmain()でセット
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.rect = self.image.get_rect()
        self.rect.center = pos  # 中心座標をposに
    def update(self):
        self.rect.move_ip(0, -self.speed)  # 上へ移動
        if self.rect.top < 0:  # 上端に達したら除去
            self.kill()

Shotオブジェクトを作成すると__init__()が呼ばれますが、そのときShotオブジェクトの位置を引数posで与えているのがポイントです。Shotオブジェクトの中心座標をposにしていますね。実際は、プレイヤーがスペースキーを押したときに自機の座標をposに与えてShotオブジェクトを作成します。すると、自機の中心からミサイルが発射されるようになります。update()では、speedの速さで上へ移動するようにしてます。そして、上端に達したらkill()を呼び出してShotオブジェクトを破棄します。

ここでのポイントは、プレイヤーがスペースキーを押すたびに新しいShotオブジェクトが自機の場所に生成されてupdate()で上へすっ飛んでいきます。そして、上端に達したらそのオブジェクトは破棄されます。ミサイルは使い捨てで再利用しません。

(注)オブジェクトの生成は少し時間がかかる処理なので使い捨てせずに再利用する方が効率がよいです。

Shotのデフォルトスプライトグループcontainersと画像imageはmain()で設定しています。これらの属性はShotオブジェクトを作成する前に設定する必要があります。デフォルトスプライトグループについてはスプライトグループの使い方(2008/5/17)を参照してください。

    # スプライトグループを作成して登録
    all = pygame.sprite.RenderUpdates()
    Player.containers = all
    Shot.containers = all
    # スプライトの画像を登録
    Player.image = load_image("player.png")
    Shot.image = load_image("shot.png")

imageはShotのクラス変数としているのですべてのShotオブジェクトが共有します。つまり、いくらミサイルを発射しても画像がロードされるのは1回だけすみます。

ミサイルの発射

次にミサイルが発射される処理を実装します。これは、Playerクラスのupdate()に書きました。

class Player(pygame.sprite.Sprite):
    """自機"""
    speed = 5  # 移動速度
    reload_time = 15  # リロード時間
    def __init__(self):
        # imageとcontainersはmain()でセット
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.rect = self.image.get_rect()
        self.rect.bottom = SCR_RECT.bottom  # プレイヤーが画面の一番下
        self.reload_timer = 0
    def update(self):
        # 押されているキーをチェック
        pressed_keys = pygame.key.get_pressed()
        # 押されているキーに応じてプレイヤーを移動
        if pressed_keys[K_LEFT]:
            self.rect.move_ip(-self.speed, 0)
        elif pressed_keys[K_RIGHT]:
            self.rect.move_ip(self.speed, 0)
        self.rect.clamp_ip(SCR_RECT)
        # ミサイルの発射
        if pressed_keys[K_SPACE]:
            # リロード時間が0になるまで再発射できない
            if self.reload_timer > 0:
                # リロード中
                self.reload_timer -= 1
            else:
                # 発射!!!
                Player.shot_sound.play()
                Shot(self.rect.center)  # 作成すると同時にallに追加される
                self.reload_timer = self.reload_time

スペースキーを押すとミサイルが発射されます。ミサイルの発射はShotオブジェクトを作るだけです。Shotにはデフォルトスプライトグループ allを設定してあるので自動的にスプライトグループに追加され描画、更新が始まります。Shot()の引数にプレイヤーの中心座標を与えているのでミサイルはプレイヤーの中心に作成されます。他の座標を指定すればそこからミサイルが発射されます(試してみてください)。

ミサイルの発射には、リロード(弾込め)に一定時間かかるようにします。リロードなしだとものすごい数のミサイルが一気に発射できますがちょっとおかしいです(試してみてください)。リロード時間は、reload_timerという変数で管理します。一回発射すると reload_time=15(timerではない!)という値がreload_timerにセットされます。このreload_timerが0より大きい間は再発射できません。時間が経つと1ずつ小さくなっていき、0になった時点でリロード完了、再発射できるようになります。

(注)スペースキーを受け付ける処理をPlayerのupdate()に書いたのでスペースキーを押した瞬間にupdate()が呼ばれてないとミサイルが発射されません。スペースキーを押した瞬間にミサイルが出ないことがあるのはこのためです。もし押した瞬間に必ず出るようにしたければ main()のイベントハンドラに発射処理を書く必要があります。まあスペースキー押しっぱなしにすれば問題ないのですが。

ミサイルが発射されたときに効果音が鳴るようにしました。効果音のロードはmain()です。Playerクラスのクラス変数にしましたが、Shotクラスに持たせた方がよかったかも。効果音はplay()で鳴らせます。詳しくはBGMと効果音の再生(2008/5/11)を参照してください。

    # サウンドのロード
    Player.shot_sound = load_sound("shot.wav")