ブロックくずしを作る9「ブロックと球の衝突処理2」

ブロックに球との衝突判定を追加する

衝突判定のアルゴリズムができたので、実際にコーディングしていきましょう。

まずはブロック(Brickクラス)に追加するコードです。

export class Brick implements IGameCanvasPart {
    /**
     * ブロックと球が衝突するかチェックする。
     * 衝突時には、衝突によって球の速度を変更するための加速度情報を算出して返す。
     * @param ball 
     * @returns 衝突情報
     */
    public CollisionBall(ball: Ball) {
        // 衝突チェック
        let result = CollisionCircleToRectangle(ball.Circle, ball.VX, ball.VY, this._body);
        let collison = new CollisionBallResult();
        // 衝突位置に応じて、加速度を算出し、衝突情報をセット
        switch (result) {
            case "HitCorner":       // 角に衝突
                collison.Result = true;             // 衝突発生時はtrue
                collison.Object = this;             // 衝突した物体
                collison.AX = -ball.VY - ball.VX;   // X方向の速度に対する加速度
                collison.AY = -ball.VX - ball.VY;   // Y方向の速度に対する加速度
                break;
            case "HitLeftRight":    // 左辺・右辺に衝突
                collison.Result = true;
                collison.Object = this;
                collison.AX = -ball.VX - ball.VX;
                collison.AY = 0;
                break;
            case "HitTopBottom":    // 上辺・下辺に衝突
                collison.Result = true;
                collison.Object = this;
                collison.AX = 0;
                collison.AY = -ball.VY - ball.VY;
                break;
            case "NotHit":          // 衝突していない
                collison.Result = false;
                break;
        }
        return collison;
    }
}

CollisionBall()は、球とブロックとの衝突を判定します。衝突判定に、ブロックくずしを作る8「ブロックと球の衝突処理」で作ったCollisionCircleToRectangle()を使っています。

そして、衝突が起きている場合は、球の移動方向の変化(=加速度)を算出します。それらの情報をまとめたCollisionBallResultクラスを返します。

CollisionBallResultクラスのコードを下に記載します。

import { Brick } from './Brick';

/**
 * 球の衝突判定の結果を保持するクラス
 */
export class CollisionBallResult {
    /**
     * 球が何かに衝突したか否か
     * true: 何かに衝突した false: 衝突していない
     */
    public Result = false;

    /**
     * 衝突した物体
     */
    public Object: Brick | undefined;

    /**
     * 衝突の結果として、球の速度のX成分を変更する値(加速度)
     */
    public AX = 0;

    /**
     * 衝突の結果として、球の速度のY成分を変更する値(加速度)
     */
    public AY = 0;
}

ブロックセットに球との衝突判定を追加する

全てのブロックはブロックセット(BrickSetクラス)で管理しているので、ブロックセットに球との衝突判定を追加します。

/**
 * ブロックセットクラス
 * 
 * 全てのBrickを管理するクラス
 */
export class BrickSet implements IGameCanvasPart {
    /**
     * 球とブロックの衝突を判定する。
     * @param ball 
     */
    public CollisionBall(ball: Ball) {
        for (let i = 0; i < this._brickList.length; i++) {
            let brick = this._brickList[i];
            let collision = brick.CollisionBall(ball);
            // 球が衝突したブロックがあれば、その情報を返す。
            if (collision.Result) {
                return collision;
            }
        }
        
        // 球が全てのブロックと衝突しなかったとき
        let collison = new CollisionBallResult();
        collison.Result = false;
        return collison;
    }
}

単純にforループを使って全ブロックに対して衝突を判定し、衝突した場合はその判定結果を返しています。

そしてすべてのブロックが衝突しなかった場合は、衝突が無いという結果を返しています。

球にブロックとの衝突処理を追加する

最後に、球(Ballクラス)にブロックとの衝突処理を追加します。

/**
 * 球を表すクラス
 */
export class Ball implements IGameCanvasPart {
    /**
     * 球本体の円
     */
    private _body = new Circle(0, 0, GameParameter.Ball.Size);
    public get Circle() { return this._body as Readonly<Circle> };

    /**
     * X座標の速度
     */
    private _VX = 0;
    /**
     * X座標の速度
     */
    public get VX() { return this._VX; }

    /**
     * Y座標の速度
     */
    private _VY = 0;
    /**
     * Y座標の速度
     */
    public get VY() { return this._VY; }

    /**
     * 状態更新
     */
    public Update() {
        let gameArea = this._game.Area;
        if (gameArea === undefined) { return }
        if (this._game.Status == "game") {
            let N = 10;
            for (let i = 0; i < N; i++) {
                // 衝突を考慮せずに、座標と速度を更新する。
                this._body.X += this._VX / N;
                this._body.Y += this._VY / N;

                // ブロックとの衝突処理
                this._CollisionBrick();

                // ラケットとの衝突処理
                this._CollisionRachket();

                // 左右の壁との衝突処理
                if (this._VX < 0 && this._body.Left < gameArea.Left) {
                    // X座標速度を反転する。
                    this._VX = -this._VX;
                }
                else if (this._VX > 0 && this._body.Right > gameArea.Right) {
                    // X座標速度を反転する。
                    this._VX = -this._VX;
                }

                // 天井との衝突処理
                if (this._VY < 0 && this._body.Top < gameArea.Top) {
                    // Y座標速度を半分にして反転する。
                    this._VY = -this._VY;
                }

                // 床との衝突処理
                if (this._body.Bottom > gameArea.Bottom) {
                    this._game.Status = "gameover";
                }
            }
        }
    }

    /**
     * ブロックとの衝突処理
     */
    private _CollisionBrick(){
        let collision = this._game.BrickSet.CollisionBall(this);
        if (collision.Result) {
            // 衝突したので球の速度を変更する
            this._VX += collision.AX;
            this._VY += collision.AY;
        }
    }
}

Update()の中で、壁やラケットととの衝突処理を行っているので、同様にブロックとの衝突処理をUpdate()に追加しました。(42-43行目)

ブロックとの衝突処理の本体は45行目の_CollisionBrick()メソッドです。前述したブロックセットとの衝突判定を行い、結果に従ってブロックの速度を変更しています。

また、その他の変更点として、9行目のCircle()、18行目のVX()、27行目のVY()メソッドを追加しました。これらは、Brick.CollisionBall()メソッドの中でブロックと球の衝突判定を行うときに、球の位置や大きさ、速度などを参照する必要があるため追加しています。

コメント