【連載】ジャンプ＆ランゲームを作ろう(5) ボールとブロックの判定処理

# はじめに

こちらのQiitaは勝手連載「ジャンプ＆ランゲームを作ろう」になります。
初回の記事はこちらです。
[【連載】ジャンプ＆ランゲームを作ろう(1) プロジェクト作成](https://qiita.com/motoyasu-yamada/items/3fcc164cae23537b5f1a)
※お断り※ 毎日お昼休みに15分程度で執筆していおり細切れになりましてすみません


# 今日の内容
前回のボールの処理は床の位置(Y座標)が0である前提の手抜きコードでした。
次回以降、床ブロックを上下に移動させたり、床ブロックのない穴を実装したいので、床とボールの判定をちゃんと実装します。

床とボールの判定をする理由は次の２つです

1. 床に設置していない時は重力加速度を適用する
2. ジャンプするのは床に設置しているときだけにする。

# 今回のソースコード
https://github.com/liberapp-inc/h5g-qiita-jump-and-run/tree/0ba7b9045f070afec054803047fbce7303660317

コミット内容(前回との差分)：
https://github.com/liberapp-inc/h5g-qiita-jump-and-run/commit/0ba7b9045f070afec054803047fbce7303660317

# 解説

毎フレームのBallの更新処理を行う`updateBall`関数が今回の変更点です

```ts:Main.ts
    private updateBall() {        
        this.ball.x += this.ballVx;
        this.ball.y += this.ballVy;

        let isBallOnBlock: boolean;
        const ballRect = this.ball.getTransformedBounds(this.world);
        const bottom = this.ball.y;
        const blockRect= new egret.Rectangle();
        this.platforms.forEach(e => {
            e.getTransformedBounds(this.world,blockRect);
            if (blockRect.intersects(ballRect)) {
                const blockCenterY = (blockRect.top + blockRect.bottom) / 2;
                if (bottom <= blockCenterY) {
                    isBallOnBlock = true;
                    this.ball.y = blockRect.top;
                    this.ballVy = 0;
                }
            }
        });

        if (isBallOnBlock) {
            this.jumpCount = 0;
        } else {
            this.ballVy += 0.1;
        }
        if ((isBallOnBlock || this.jumpCount < 2) && this.tapped) {
            this.ballVy -= 5;
            this.jumpCount ++;
        }  
        this.tapped = false;
    }
```

`isBallOnBlock`変数で、BallがBlockの上に乗っているか、空中に浮いているかを管理します。

```ts
let isBallOnBlock: boolean;
```

`getTransformedBounds`メソッド(API)で、Ballの表示領域の矩形を取得します。

引数で渡した`this.world`に対しての相対の位置になります。

```ts
        const ballRect = this.ball.getTransformedBounds(this.world);
```

こちらのイディオムですべての床ブロックについて処理をくりかえします。

```ts
        this.platforms.forEach(e => {
            :
        });
```

`intersects`でBallの領域矩形`ballRect`と`blockRect`の領域矩形に接するところがあるかを判定します。

もし矩形が接する場合に、ブロックの重心Y座標(blockCenterY)よりボールの底辺が上にある場合は、ボールは床ブロックの上に接地していると判断します。


```ts
            if (blockRect.intersects(ballRect)) {
                const blockCenterY = (blockRect.top + blockRect.bottom) / 2;
                if (bottom <= blockCenterY) {
:
                }
            }
```

もし設置していた場合は、フラグ`isBallOnBlock`をオンにします。次にめりこみを解消するために、ボールのY座標を調整します。また接地したことからボールのY方向の速度は0にします。

```ts
                    isBallOnBlock = true;
                    this.ball.y = blockRect.top;
                    this.ballVy = 0;
```

今回は二段ジャンプ流派ということで、空中ジャンプは一回目までOKとします。

そのために床に接地している場合は、ジャンプカウントをリセットし、空中でももう一回ジャンプできるようにします。
床に接地していない場合は、ボールに重力加速度を適用します。

床に接地している、もしくはジャンプカウントが1回以下の場合に、タップした場合は、上方向の加速度を適用して、ジャンプします。

```ts
        if (isBallOnBlock) {
            this.jumpCount = 0;
        } else {
            this.ballVy += 0.1;
        }
        if ((isBallOnBlock || this.jumpCount < 2) && this.tapped) {
            this.ballVy -= 5;
            this.jumpCount ++;
        }  
        this.tapped = false;
```