人工知能に関する断創録

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

二段ジャンプ

二段ジャンプというのはジャンプ中に空中でもう一回ジャンプできるアクションです。スペースキーでジャンプして空中にいる間にもう一度スペースキーを押すとさらにジャンプできます。これで高いところにも上れます。

double_jump.zip
f:id:aidiary:20100812210722p:plain

ジャンプ回数

変更はPythonクラスです。何回ジャンプしたかをカウントしておくself.jump_countと最大ジャンプ回数を定義するMAX_JUMP_COUNTを用意します。

class Python(pygame.sprite.Sprite):
    """パイソン"""
    MOVE_SPEED = 2.5    # 移動速度
    JUMP_SPEED = 6.0    # ジャンプの初速度
    GRAVITY = 0.2       # 重力加速度
    MAX_JUMP_COUNT = 2  # ジャンプ段数の回数
    
    def __init__(self, pos, blocks):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = self.right_image
        self.rect = self.image.get_rect()
        self.rect.x, self.rect.y = pos[0], pos[1]  # 座標設定
        self.blocks = blocks  # 衝突判定用
        
        # ジャンプ回数
        self.jump_count = 0
        
        # 浮動小数点の位置と速度
        self.fpx = float(self.rect.x)
        self.fpy = float(self.rect.y)
        self.fpvx = 0.0
        self.fpvy = 0.0
        
        # 地面にいるか?
        self.on_floor = False

ジャンプするたびにjump_countを+1します。ただし、MAX_JUMP_COUNTに達したらジャンプできなくします。今回は、MAX_JUMP_COUNTを2としているので二回だけジャンプできるようになります。もし、三段ジャンプにしたければMAX_JUMP_COUNTを3とすればいいです。このjump_countは下のように着地したときに0にリセットします。

    def collision_y(self):
        """Y方向の衝突判定処理"""
        # パイソンのサイズ
        width = self.rect.width
        height = self.rect.height
        
        # Y方向の移動先の座標と矩形を求める
        newy = self.fpy + self.fpvy
        newrect = Rect(self.fpx, newy, width, height)
        
        # ブロックとの衝突判定
        for block in self.blocks:
            collide = newrect.colliderect(block.rect)
            if collide:  # 衝突するブロックあり
                if self.fpvy > 0:    # 下に移動中に衝突
                    # めり込まないように調整して速度を0に
                    self.fpy = block.rect.top - height
                    self.fpvy = 0
                    # 下に移動中に衝突したなら床の上にいる
                    self.on_floor = True
                    self.jump_count = 0  # ジャンプカウントをリセット
                elif self.fpvy < 0:  # 上に移動中に衝突
                    self.fpy = block.rect.bottom
                    self.fpvy = 0
                break  # 衝突ブロックは1個調べれば十分
            else:
                # 衝突ブロックがない場合、位置を更新
                self.fpy = newy
                # 衝突ブロックがないなら床の上にいない
                self.on_floor = False

二段ジャンプ

二段ジャンプの処理は、Pythonクラスのupdate()です。

    def update(self):
        """スプライトの更新"""
        # キー入力取得
        pressed_keys = pygame.key.get_pressed()
        
        # 左右移動
        if pressed_keys[K_RIGHT]:
            self.image = self.right_image
            self.fpvx = self.MOVE_SPEED
        elif pressed_keys[K_LEFT]:
            self.image = self.left_image
            self.fpvx = -self.MOVE_SPEED
        else:
            self.fpvx = 0.0
        
        # ジャンプ
        if pressed_keys[K_SPACE]:
            if self.on_floor:
                self.fpvy = - self.JUMP_SPEED  # 上向きに初速度を与える
                self.on_floor = False
                self.jump_count = 1
            elif not self.prev_button and self.jump_count < self.MAX_JUMP_COUNT:
                self.fpvy = -self.JUMP_SPEED
                self.jump_count += 1
            
        # 速度を更新
        if not self.on_floor:
            self.fpvy += self.GRAVITY  # 下向きに重力をかける
        
        self.collision_x()  # X方向の衝突判定処理
        self.collision_y()  # Y方向の衝突判定処理
        
        # 浮動小数点の位置を整数座標に戻す
        # スプライトを動かすにはself.rectの更新が必要!
        self.rect.x = int(self.fpx)
        self.rect.y = int(self.fpy)
        
        # ボタンのジャンプキーの状態を記録
        self.prev_button = pressed_keys[K_SPACE]

地面にいない場合でもjump_countがMAX_JUMP_COUNTより小さければさらにジャンプできる(上向きの速度を加える)処理を追加しています。ここで、self.prev_buttonというのは前回のフレームでのジャンプキーの状態です。二段ジャンプをするには一度ジャンプキーを離してからもう一度押す必要があります。つまり、ジャンプキーをずっと押しっぱなしにしていた場合は二段ジャンプになりません。not self.prev_buttonという条件を外してみるとどうして必要かわかると思います。MAX_JUMP_COUNTをいろいろ変えてみると面白いと思います。