JavaScriptでブロック崩しを作成する方法を現役エンジニアが解説【初心者向け】

初心者向けにJavaScriptでブロック崩しを作成する方法について解説しています。グラフィックを描画するHTML要素のCanvasを使ったJavaScriptのゲームの例を紹介します。作成の手順と各処理の内容をサンプルコードで見ていきましょう。

TechAcademyマガジンはオンラインのプログラミングスクールTechAcademy [テックアカデミー]が運営。初心者向けに解説した記事が4,000以上あります。現役エンジニアの方はこちらをご覧ください。

JavaScriptでブロック崩しを作成する方法について、TechAcademyのメンター(現役エンジニア)が実際のコードを使用して初心者向けに解説します。

 

そもそもJavaScriptについてよく分からないという方は、JavaScriptとは何なのかについて解説した記事を読むとさらに理解が深まるでしょう。

 

なお本記事は、TechAcademyのオンラインブートキャンプJavaScript/jQuery講座の内容をもとにしています。

 

田島悠介

今回は、JavaScriptに関する内容だね!

大石ゆかり

どういう内容でしょうか?

田島悠介

ブロック崩しを作成する方法について詳しく説明していくね!

大石ゆかり

お願いします!

 

Canvasとは

Canvasとは、グラフィックやアニメーションを描画するHTML要素です。

JavaScriptで図形を描画して動かすことで、ゲームを作成することも可能です。

また、今回はCanvasを使ったブロック崩しの作成方法を紹介していくため、その使い方を学習していきましょう。

 

JavaScriptでブロック崩しを作成する方法

JavaScriptにはCanvasのAPI(canvasを操作する部品)が用意あります。

CanvasのAPIを使って四角や丸を書いて、動きの処理を作成すればブロック崩しが作成できるでしょう。

CanvasのAPIは下記の3つの手順で使えるようになります。

  1. HTMLでcanvasタグを記述する
  2. JavaScriptでcanvasタグを取得する
  3. 取得したcanvasタグ(HTMLCanvasElement)のgetContextメソッドを呼び出す

今回作成するブロック崩しは3D(立体)ではなく2D(平面)であるたる、getContextメソッドの引数には2dを指定してください。

getContextの戻り値であるCanvasRenderingContext2Dから、図形を書くためのいろいろなメソッドを呼び出すことが可能です。

 

[PR] JavaScript・jQueryで挫折しない学習方法を動画で公開中

実際に書いてみよう(サンプルコード)

サンプルコードで下記の画像のようなブロック崩しを作成します。

HTMLでcanvasタグを記述

<canvas id="canvas" width="480" height="400"></canvas>

JavaScriptで取得するためにidにcanvasという名前を設定しています。

CSSでcanvasタグの領域を黒くする

canvas { background: black; }

実際には、この記述はなくても動作に影響はありません。

canvasタグの領域をわかりやすくするために黒くしています。

 

メインのJavaScriptのソースコードをみていみていきましょう。

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");

/********************
 ボールの設定
 ********************/
const BALL_COLOR = 'skyblue'; //ボールの色
const BALL_RADIUS = 10; //ボールの大きさ(半径)
let ballX = canvas.width / 2; //ゲーム開始時のX軸(←→)の位置
let ballY = canvas.height - 30; //ゲーム開始時のY軸(↑↓)の位置
let xMove = 2;  //X方向への移動量
let yMove = 2;  //Y方向への移動量

/********************
 操作する板の設定
 ********************/
const BAR_COLOR = 'blue'; //板の色
const BAR_HEIGHT = 10;  //板の高さ
const BAR_WIDTH = 75; //板の幅
let barX = (canvas.width - BAR_WIDTH) / 2;
let rigthKeyFlag = false;
let leftKeyFlag = false;

/********************
 ブロックの設定
 ********************/
const BLOCK_COLOR = 'orange'
const BLOCK_ROW_COUNT = 3; //ブロックの縦の数
const BLOCK_COLUMN_COUNT = 5; //ブロックの横の数
const BLOCK_WIDTH = 75; //ブロックの幅
const BLOCK_HEGHT = 20; //ブロックの高さ
const BLOCK_MARGIN = 10; //ブロックとブロックの隙間
const BLOCK_AREA_MARGIN = 30; //ブロックを並べる領域と画面の隙間
let blockArray = []; //配置したブロック位置の配列
for(let i = 0; i < BLOCK_ROW_COUNT * BLOCK_COLUMN_COUNT; i++){
  let row = Math.floor(i/BLOCK_COLUMN_COUNT);
  let column = i - (BLOCK_COLUMN_COUNT * row); 
  blockArray.push( {
    x : BLOCK_AREA_MARGIN + BLOCK_MARGIN * column + BLOCK_WIDTH * column,
    y : BLOCK_AREA_MARGIN + BLOCK_MARGIN * row +  BLOCK_HEGHT * row
    } );
}

function drawBall() {
  ballX += xMove;
  ballY += yMove;
  ctx.beginPath();
  ctx.arc(ballX, ballY, BALL_RADIUS, 0, Math.PI * 2);
  ctx.fillStyle = BALL_COLOR;
  ctx.fill();
  ctx.closePath();
  if (ballX + xMove > canvas.width - BALL_RADIUS || ballX + xMove < BALL_RADIUS) {
    xMove = -xMove;
  }
  if (ballY + yMove < BALL_RADIUS) {
    yMove = -yMove;
  } else if (ballY + yMove > canvas.height - BALL_RADIUS) {
    if (ballX > barX && ballX < barX + BAR_WIDTH) {
      yMove = -yMove;
    } else {
      console.log("ゲームオーバー");
      clearInterval(interval)
    }
  }
}

function drawBlock() {
  blockArray = blockArray.filter( (block, index) => {
    if (ballX > block.x && ballX < block.x + BLOCK_WIDTH && ballY > block.y && ballY < block.y + BLOCK_HEGHT) {
      yMove = -yMove;
    }else{
      return block;
    }
  });
  if(blockArray.length === 0){
    console.log('ゲームクリア');
    clearInterval(interval);
    return;
  }
  blockArray.forEach((block) => {
    ctx.beginPath();
    ctx.rect(block.x, block.y, BLOCK_WIDTH, BLOCK_HEGHT);
    ctx.fillStyle = BLOCK_COLOR;
    ctx.fill();
    ctx.closePath();
  });
}

function drawBar() {
  if (rigthKeyFlag && barX < canvas.width - BAR_WIDTH) {
    barX += 7;
  } else if (leftKeyFlag && barX > 0) {
    barX -= 7;
  }
  ctx.beginPath();
  ctx.rect(barX, canvas.height - BAR_HEIGHT, BAR_WIDTH, BAR_HEIGHT);
  ctx.fillStyle = BAR_COLOR;
  ctx.fill();
  ctx.closePath();
}

function keyDownHandler(e) {
  if (e.key == "Right" || e.key == "ArrowRight") {
    rigthKeyFlag = true;
  } else if (e.key == "Left" || e.key == "ArrowLeft") {
    leftKeyFlag = true;
  }
}

function keyUpHandler(e) {
  if (e.key == "Right" || e.key == "ArrowRight") {
    rigthKeyFlag = false;
  } else if (e.key == "Left" || e.key == "ArrowLeft") {
    leftKeyFlag = false;
  }
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawBall();
  drawBlock();
  drawBar();
}

document.addEventListener("keydown", keyDownHandler);
document.addEventListener("keyup", keyUpHandler);
let interval = setInterval(draw, 10);

解説

最初に「document.getElementById(“canvas”)」にてHTMLのcanvasを取得しました。

「canvas.getContext(“2d”)」で、取得したcanvasから図形を描画するための2D用のコンテキストという部品を取得し、描画の準備が整えます。

画面にはボールと操作する板とブロックの3つを描画するため、それぞれの色やサイズを変数に格納しています。

変数に格納することで、後から動作や見た目を変更するような場合に楽に対応することが可能です。

 

ブロックは5個並べたものを3列としており、blockArrayという配列にX座標とY座標の値をもつオブジェクトを15個格納しています。

図形を描画したり動かす際には、位置の指定や変更が必要です。

その位置を表すのがX座標とY座標であり、キャンバスの左上がX軸が0、Y軸が0です。

右へ行くほどX軸が大きくなり、縦軸を表すY軸は下に行くほど大きくなります。

下記の画像のような感じになるため、赤丸の座標を確認してみましょう。

それでは実際の図形を描画する処理を見てみましょう。

ボールを描画する関数

「function drawBall()」がボールを描画する関数です。

この関数は後ほど説明する繰り返し処理により、10ミリ秒ごとに呼び出しが行われます。

「ballX += xMove」と「ballY += yMove」にて、X軸とY軸の値が足されることで10ミリ秒ごとに移動するアニメーションになります。

丸の書き方は、「ctx.beginPath()」から「ctx.closePath()」の間の記述です。

ctxは最初に取得した2D図形を書くコンテキストです。

 

図解を書く際にはbeginPathとclosePathで挟むようにします。

これは無くても動作するものの、図形の描画がその中で完結することで、記述した図形の色やサイズが残ってしまうミスを防ぐことができるため安全です。

「ctx.arc(ballX, ballY, BALL_RADIUS, 0, Math.PI * 2)」にて丸を書いています。

arcというメソッドに5つ(X軸の値、Y軸の値、円のサイズ(半径)、円を書き始める角度、円を書き終わる角度)を指定することで円を描写できます。

円を書いた後は、円の位置(ballX、ballY)をif文で判定し、キャンバスの端でボールの跳ね返したり、キャンバスの下に着いてしまったらゲームオーバーにしてみましょう。

跳ね返す動きは、座標に足しているxMoveとyMoveという変数をマイナスにすることで反対に動くようになります。

 

ブロックを描画する関数

「function drawBlock() 」が、ブロックを描画する関数です。

blockArrayという変数にブロックのX軸とY軸の値が入ったオブジェクトが配列で格納されているので、これを使って処理を行いましょう。

最初に「blockArray.filter」で、ボールの座標と比較してボールとぶつかったブロックを消しています。

次に「if(blockArray.length === 0)」にてブロックがなくなったらゲームクリアです。

最後に「blockArray.forEach((block)」にて、ブロックを描画しています。

 

円を書くときと同様に、「ctx.beginPath()」から「ctx.closePath()」で挟みます。

ブロックは四角なので「ctx.rect(block.x, block.y, BLOCK_WIDTH, BLOCK_HEGHT)」で四角を書きます。

また、rectメソッドに4つの数値(X軸の値、Y軸の値、四角の幅、四角の高さ)を指定することで四角を表すことが可能です。

 

板を描画する関数

「function drawBar()」は板を描画する関数です。

キーを押している間だけ、X軸の値が増減するようになっています。

この後ででてくるキーボードのイベント処理により、右矢印と左矢印を押している間の判定がフラグでできるようになります。

板の描画はブロックと同じく四角を記述しましょう。

 

キーボードの操作を処理する関数

「function keyDownHandler(e)」と「function keyUpHandler(e)」がキーボードの操作を処理しています。

処理の内容は、右矢印と左矢印を押している間だけフラグを立てているだけです。

引数のeがイベントを格納しているため、「e.key」とすることで押したキーを取得可能です。

この2つの関数は、ソースの最後の方にあるaddEventListenerにより、keydown(キーがおされた)・keyup(キーが離された)イベントが紐付いています。

 

描画を実行する関数

「function draw()」がボールとブロックと板を描画する関数を呼び出し、画面に表示しています。

この関数はソースの最後の「setInterval(draw, 10)」により10ミリ秒ごとに呼び出され、その度に図形を描画するものです。

ボールや板は座標が変わって描画されることで動いているように描写することが可能です

「clearRect」はキャンバスをクリアするメソッドです。

これにより、何度描画してとしても、前回の呼び出しで記述した図形が消えて新しく書き直さる処理となっています。

 

執筆してくれたメンター

横山茂雄(よこやましげお)

フリーエンジニアとして活動中。

サーバーサイドからフロントまで時代の波に合わせてスキルを変化させてきました。

言語、フレームワーク、DB、現場、いずれも転々としながら、筋トレも欠かさない体育会系エンジニアです。

TechAcademyジュニアのゲームアプリコースを担当しています。

 

大石ゆかり

JavaScriptでブロック崩しを作成する方法がよく分かったので、良かったです!

田島悠介

ゆかりちゃん、これからも分からないことがあったら質問してね!

大石ゆかり

分かりました。ありがとうございます!

 

TechAcademyでは、初心者でも最短4週間でJavaScript・jQueryを使ったWebサービス公開を習得できるオンラインブートキャンプを開催しています。

また、現役エンジニアから学べる無料体験も実施しているので、参加してみてください。