「ブロック崩し」(Breakout)は「ゲームカタログ@Wiki」によると、次のように説明されています。
目次
特徴・評価点
ゲームとしては「パドル(つまみ型)コントローラーでパドルを操作し、ボールを跳ね返して画面上部にあるブロックを破壊する」という、本当に一般的なブロック崩しの原型のようなもので、特別表記するような事はない。その位に本作は今のブロック崩しの土台をほぼ完全に築き上げていた存在であった。
以下は、このBreakoutもどきの、p5.playのサンプルにある「Breakout」を見ていきます(なお、細かな部分でコードを書き替えています)。
// 「ブロック崩し」もどき(コアの仕組みのみ)
// パドルはマウスでコントロール、クリックでスタート
let paddleSp;
let ballSp;
let topWallSp;
let bottomWallSp;
let leftWallSp;
let rightWallSp;
let bricksGroup;
const maxSpeed = 9;
// 四方の壁の厚み
const wallThickness = 10;
// ブロックの幅
const brickWidth = 40;
// ブロックの高さ
const brickHeight = 20;
// ブロック間の空き
const gutter = 4;
// 並べるブロックの行数
const rows = 9;
// 並べるブロックの列数
const columns = 16;
function setup() {
createCanvas(800, 600);
// 四方の壁スプライトを作成
const wallColor = color(0);
topWallSp = createSprite(width / 2, wallThickness / 2, width + wallThickness * 2, wallThickness)
topWallSp.shapeColor = wallColor;
// 重くて動かない
topWallSp.immovable = true;
bottomWallSp = createSprite(width / 2, height - wallThickness / 2, width + wallThickness * 2, wallThickness);
bottomWallSp.shapeColor = wallColor;
bottomWallSp.immovable = true;
leftWallSp = createSprite(wallThickness / 2, height / 2, wallThickness, height);
leftWallSp.shapeColor = wallColor;
leftWallSp.immovable = true;
rightWallSp = createSprite(width - wallThickness / 2, height / 2, wallThickness, height);
rightWallSp.shapeColor = wallColor;
rightWallSp.immovable = true;
// パドルスプライトを作成
paddleSp = createSprite(width / 2, height - 50, 100, 10);
paddleSp.immovable = true;
// ブロックのグループ
bricksGroup = new Group();
// 幅800のキャンバスに、ブロックを16列並べるために、ブロックを水平方向にいくつずらすか
const offsetX = (width - (columns * brickWidth) + ((columns - 1) * gutter)) / 2 - brickWidth;
// 高さ600のキャンバスに、ブロックを9行並べるために、ブロックを垂直方向にいくつずらすか
const offsetY = 80;
// rows x columns = 9 x 16にブロックスプライトを並べる
// 上の行から開始
for (let r = 0; r < rows; r++)
// 横にブロックを並べる
for (let c = 0; c < columns; c++) {
// ブロックのセンターは、(c * (brickWidth + gutter), r * (brickHeight + gutter))にそれぞれ、offsetXとoffsetYを加えたもの
const brickSp = createSprite(offsetX + c * (brickWidth + gutter), offsetY + r * (brickHeight + gutter), brickWidth, brickHeight);
brickSp.shapeColor = color(255, 255, 255);
brickSp.immovable = true;
bricksGroup.add(brickSp);
}
// ボールスプライトの作成
ballSp = createSprite(width / 2, height - 200, 11, 11);
ballSp.maxSpeed = maxSpeed;
paddleSp.shapeColor = ballSp.shapeColor = color(255, 255, 255);
}
function draw() {
background(247, 134, 131);
// パドルスプライトは水平方向にしか移動できず、キャンバス外には出られない
paddleSp.position.x = constrain(mouseX, paddleSp.width / 2, width - paddleSp.width / 2);
// ボールスプライトは四方の壁と跳ね返る
ballSp.bounce(topWallSp);
ballSp.bounce(bottomWallSp);
ballSp.bounce(leftWallSp);
ballSp.bounce(rightWallSp);
// ボールスプライトがパドルスプライトと重なったら、
if (ballSp.bounce(paddleSp)) {
// ボールスプライトは「反射の法則」で跳ね返るが、ボールの芯とパドルの芯のずれが影響する
const swing = (ballSp.position.x - paddleSp.position.x) / 3;
ballSp.setSpeed(maxSpeed, ballSp.getDirection() + swing);
}
// ボールスプライトがブロックスプライトに重なると、brickHit()関数が呼び出される
ballSp.bounce(bricksGroup, brickHit);
drawSprites();
}
// マウスを押してスタート
function mousePressed() {
if (ballSp.velocity.x == 0 && ballSp.velocity.y == 0)
ballSp.setSpeed(maxSpeed, random(90 - 10, 90 + 10));
}
// ボールスプライトがブロックスプライトに重なったら、
function brickHit(ball, brick) {
// ブロックスプライトを削除
brick.remove();
}
次のリンクのクリックで実際にゲームサンプルがプレイできます。「ブロック崩し」
四方を壁のスプライトで囲む方法や、ボールスプライトがブロックスプライトに当たって跳ね返るときの手法(変数swingの使用)は、これまでのゲームサンプルで見てきたものと変わりません。ほとんどのことはp5.play.jsの機能で自動的に処理できます。
このサンプルのブロックのように、スプライトをタイル状に並べるときに用いられるテクニックはほぼ共通しています。
必要なのは、キャンバスの幅(width)と高さ(height)に対して、「ブロック崩し」ゲームに適切なブロックの幅と高さを決め、ブロック間の隙間(空き)と、ブロックを縦と横にいくつ並べるかを決めることです。下図はそれを示したものです(上記サンプルコードで使われている変数名と数値を併記しています)。
スプライトをタイル状に並べるには、配置する位置をあらかじめ計算しておいてそこに1つずつ作成していくこともできますが、通常は二重のforループを用いる方が記述するコード量が少なくて済みます。
// rows x columns = 9 x 16にブロックスプライトを並べる
// 上の行から開始
for (let r = 0; r < rows; r++) {
// 横にブロックを並べる
for (let c = 0; c < columns; c++) {
const brickSp = createSprite(c * (brickWidth + gutter), r * (brickHeight + gutter), brickWidth, brickHeight);
}
}
外側のforループでは行数分だけ繰り返し、内側のforループでは列数分だけ繰り返します。このときブロックのx位置はc * (brickWidth + gutter)で、y位置はr * (brickHeight + gutter)になります。上記サンプルを上記のforループで実行すると、下図のようになります。
p5.play.jsのスプライトの(x, y)位置は左上隅ではなくセンターなので、これは当然の結果です。したがって各スプライトを適量だけ、右と左に移す必要があります。上記サンプルでこれを行うのが、変数offsetXとoffsetYです。
代入する値は、offsetYのように数値で決め打ちすることもできますし、offsetXのように関係する数値から計算して決めることもできます。
const offsetX = (width - (columns * brickWidth) + ((columns - 1) * gutter)) / 2 - brickWidth;
上記コードでは、ブロックの幅の合計(columns * brickWidth)と、空きの合計((columns – 1) * gutter))を足したものを、キャンバスの幅から引いて、それを2で割り、そこからブロックの幅を引いています。