これはゲーム開発Canvasチュートリアルの10ステップのうち6番目のステップです。このレッスンを終えたあとの完成予想のソースコードはGamedev-Canvas-workshop/lesson6.htmlで入手できます。
ゲームプレイ制御を修正することにより負けることができるようになります。この大きな変更により、ついにゲームらしさを感じられるようになりました。ですが、壁とパドルでボールが弾むだけではすぐに空きてしまいます。ブロック崩しで本当に必要な要素、それはボールで崩すことができるブロックです。これが今回作り込んでいく部分になります。
ブロック変数を設定する
このレッスンのおおまかな目標は、ブロックのための、2次元配列を走査する入れ子のループを使った数行のコードを書き上げることです。しかしその前に幅と高さ、行と列などといった情報を定義するいくつかの変数が必要です。自分のコードの、以前変数を宣言した場所の下に次のコードを追加してください。
var brickRowCount = 3;
var brickColumnCount = 5;
var brickWidth = 75;
var brickHeight = 20;
var brickPadding = 10;
var brickOffsetTop = 30;
var brickOffsetLeft = 30;
ここではブロックの行と列の数、幅と高さ、ブロックがくっつかないようにするブロック間の隙間、そしてキャンバスの端に描画されないようにするための上端、左端からの相対位置を定義しました。
1つの2次元配列で全てのブロックを記録します。2次元配列はブロックの列 (c) を含んでおり、列は行 (r) を含み、行はそれぞれのブロックが描画される画面上のx
座標とy
座標をもつオブジェクトを含んでいます。
var bricks = [];
for(var c=0; c<brickColumnCount; c++) {
bricks[c] = [];
for(var r=0; r<brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0 };
}
}
上記のコードは行と列を通してループし、新しいブロックを作ります。このブロックオブジェクトは後に衝突検出のためにも使われることを覚えておいてください。
ブロック描画ロジック
配列に含まれる全てのブロックを通してループする関数を作成し、画面上に描画しましょう。コードは次のようになります。
function drawBricks() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
bricks[c][r].x = 0;
bricks[c][r].y = 0;
ctx.beginPath();
ctx.rect(0, 0, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
もう一度、行と列を通してループし、それぞれのブロックのx
座標とy
座標を設定するとともに、1回ループを回るごとに大きさbrickWidth
x brickHeight
のブロックをCanvas上に描画しています。問題はそれら全てを1箇所、座標(0,0)
に描画していることです。それぞれのブロックのx
座標とy
座標を導出する計算を一回一回のループに含める必要があります。
var brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
var brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
それぞれの座標brickX
はbrickWidth
+ brickPadding
に列番号c
をかけ、brickOffsetLeft
をたしたものとして導出されます。brickYのロジックも同様ですが、行番号r
、brickHeight
、そしてbrickOffsetTop
が用いられます。これで、それぞれのブロックは正しい行、列に間隔を空けて置かれ、左上端から一定の位置に描画されるようになりました。
次のようにbrickX
とbrickY
の値を(0,0)
の代わりに座標として代入するようにしたものがdrawBricks()
の最終版となります。これをdrawPaddle()
関数の下に追加してください。
function drawBricks() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
var brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
var brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
ブロックを描画する
drawBricks()
へ呼び出しをdraw()
関数のどこかに追加して、このレッスンの仕上げとしましょう。最初のあたりの、Canvasを消去する部分とボールを描画する部分の間あたりが良いでしょう。drawBall()
の呼び出しのすぐ前に次の行を追加してください。
drawBricks();
自分のコードと比べる
ここまででゲームは更にもう少し面白くなりました。
練習: 行や列にあるブロックの数や位置を替えてみましょう。