JavaScript - ピンポンゲームの作り方 Part 1 - キーで操作する

JavaScript でピンポンゲームを作成します。キーで操作する方法や衝突判定、ゲームループなどを3回に分けて解説します。今回の Part 1 では、キー押下でパドルを動かせるようにコーディングします。

JavaScript - ピンポンゲームの作り方 Part 1 - キーで操作する
Photo by Feelfarbig Magazine / Unsplash

JavaScript でゲーム作成|キーを押して動かす

JavaScript のコーディングに慣れてきたら、挑戦したくなるのがゲーム作成ですよね。JavaScript で何か遊べる作品を作ってみたい!という方へ向けて、3回にわたってピンポンゲームの作り方を紹介していきます。

コードの分量が多くなるので、以下のように内容を分けて解説しますね。

  1. Part 1(この記事)では、主に、キーボードのキーを押して操作するためのコードを書いていきます。パドルを動かす方法のほか、ゲームの土台となるキャンバスの準備もここで行います。
  2. Part 2 では、ボールを動かして、壁で跳ね返ったりパドルで打ち返せるようにします。また、打ち返しに失敗したら得点が入るようにコーディングします。
  3. Part 3 では、ゲーム全体を制御するゲームループを作成し、ピンポンゲームを完成させます。

サンプルプロジェクト

最初に、ピンポンゲームの動きを確認しておきましょう。今回作成するのは、ユーザーとコンピューターが対戦するゲームです。

  • Enter キーを押すと、ゲーム開始
  • ユーザーは左のパドルを、「w」キーで上へ、「s」キーで下へ動かす
  • 右のパドルは自動で動く
ピンポンゲームのサンプル
ピンポンゲームのサンプル

ピンポンゲームは、HTML のキャンバス <canvas> に JavaScript でオブジェクト(パドルとボールと得点)を描き、アニメーションさせて作成します。JavaScript でキャンバスに描画する方法は以下の記事で詳しく解説しているので、併せてご覧ください。

JavaScript - Canvas に図形を描く方法
HTML の canvas 要素を利用すると、JavaScript でグラフィックを描くことができます。基本的なメソッドを学んで、四角形や三角形、円やテキストなど簡単な図形を描画できるようになりましょう。

HTML と CSS

まずは HTML です。

  • 描画エリア <canvas> を作成し、width 属性と height 属性でキャンバスのサイズを指定します。
<canvas id="game-canvas" width="600" height="300"></canvas>

続いて CSS です。

  • キャンバスの領域が分かるように、背景色を設定します。
canvas {
  background-color: #06488E;
  display: block;
  margin: 0 auto;
}

JavaScript

さて、次は JavaScript です。JavaScript でキャンバスに描画できるように準備をしましょう。

  • querySelector() メソッドで描画エリアを取得します。
  • 描画するための 2d コンテキストを変数 ctx で参照できるようにします。
// キャンバスに描画する準備
const canvas = document.querySelector('#game-canvas');
const ctx = canvas.getContext('2d');

これから説明していく内容は、大きく分けて次の二つです。どちらも、キーが押されたことをきっかけにして処理を実行します。

  1. キーを押してゲームを開始するコード
  2. パドルを上下に動かすコード

1 キーを押してゲームを開始するコード

今回作成するピンポンゲームは、Enter キーを押してゲームを開始します。そのためのコードを書いていきましょう。

変数|ゲーム中かどうかを示す

ゲーム中かどうかをチェックするための変数 playing を用意します。これは、Enter キーが効くのはゲームが止まっているときだけで、ゲーム中に Enter キーを押しても反応しないようにするための変数です。最初はゲーム停止中 false で初期化してください。

// ゲーム中かどうかどうか(trueまたはfalse)
let playing = false;

イベント|キーが押されたときゲームを開始する

Enter キーが押されたときにゲームを開始する処理を書いていきましょう。

まずは、イベントの追加です。addEventListener() メソッドを使って、ユーザーがキーを押したときに関数が実行されるようにします。

  • キーが押されたときは keydown イベントが発生します。呼び出す関数は startGame です。
// キーが押されたときにstartGameを呼び出す
document.addEventListener('keydown', startGame);

関数|ゲームを開始 - startGame

では、ゲームを開始する関数を定義しましょう。関数 startGame には引数 e を指定します。これは、イベントが発生したキーの情報を受け取るための変数です。

// Enterキーの押下でゲーム開始
function startGame(e) {

  // Enterキーが押されたことを確認する
  // ゲーム中ではないことを確認する
  // ゲームを開始する
  // ゲーム中であることを示す

}

Enterキーが押されたことを確認する

e に保存された情報の中から、押されたキーの値を key プロパティで取得します。「押されたキーの値 e.key が Enter なら」という if 文を書きましょう。

function startGame(e) {

  // Enterキーが押されたことを確認する
  if (e.key === 'Enter') {

    // ゲーム中ではないことを確認する
    // ゲームを開始する
    // ゲーム中であることを示す
    // …
    
  }
}

ゲーム中ではないことを確認する

Enter キーが効くのは、ゲーム停止中だけです。「ゲーム中ではないなら」という if 文を追加してください。

function startGame(e) {
  if (e.key === 'Enter') {

    // ゲーム中ではないことを確認する
    if (!playing) {

      // ゲームを開始する
      // ゲーム中であることを示す
      // …
      
    }
  }
}

ゲームを開始する

押されたキーが Enter で、さらに、ゲーム中ではないなら、関数 gameLoop を呼び出してゲームを開始します。(gameLoop は、ゲーム全体の流れを制御する関数です。詳しくは、今後 Part 3 の記事で解説します)

function startGame(e) {
  if (e.key === 'Enter') {
    if (!playing) {

      // ゲームを開始する
      gameLoop();

      // ゲーム中であることを示す
      // …
      
    }
  }
}

ゲーム中であることを示す

変数 playing の値を true にして、ゲーム中であることを示します。これによって、ゲーム開始後は Enter キーが無効になります。もし、変数 playingfalse のままでゲーム中にも Enter キーが効いてしまうと、ゲームが正常に動かなくなるので注意してくださいね。

function startGame(e) {
  if (e.key === 'Enter') {
    if (!playing) {
      gameLoop();

      // ゲーム中であることを示す
      playing = true;

    }
  }
}

以上で、Enter キーを押してゲームを開始する関数 startGame を定義できました。

2 パドルを上下に動かすコード

ここからは、パドルに関連するコードを書いていきますよ。パドルは、キャンバスの左側と右側に配置します。左側のパドルはキーボードのキーで上下に動かせるようにし、右側のパドルは自動的に動くようにコーディングしましょう。

変数|パドルのサイズ・位置・速度

最初に、パドルを描くために必要な変数を用意します。

// 幅
const paddleWidth = 10;
// 高さ
const paddleHeight = 100;
// 左パドルの位置
let leftPaddleY = canvas.height / 2 - paddleHeight / 2;
const leftPaddleX = 30;
// 右パドルの位置
let rightPaddleY = canvas.height / 2 - paddleHeight / 2;
const rightPaddleX = canvas.width - 40;
// 左パドルの速さ
const paddleVelocity = 5;

paddleVelocity は、パドルを動かすときに必要になる変数です。Velocity という英単語は移動速度(と移動方向)を意味していて、この値が大きければ大きいほどパドルを早く動かせるようになります。

キャンバスとパドルの位置関係
キャンバスとパドルの位置関係

変数|キーが押されているかどうかを示す

次は、キーが押されているかどうかを示す変数 wPressedsPressed です。左パドルを操作するための w キーと s キーが「押されている」か「押されていない」かを、true または false で表します。最初は、キーが押されていない false で初期化してください。

// wキー
let wPressed = false;
// sキー
let sPressed = false;

イベント|キーが押されたか離されたかを確認する

変数を用意できたら、パドル操作用のキーが「押された」または「離された(押下をやめた)」ことを確認する処理を書いていきます。

まずは、イベントの追加です。addEventListener() メソッドを使って、ユーザーがキーを「押した」ときと「押すのをやめた」ときに、それぞれ関数が実行されるようにします。

  • キーが押されたときは keydown イベントが発生します。呼び出す関数は keyDownHandler です。
  • キーが離されたときは keyup イベントが発生します。呼び出す関数は keyUpHandler です。
// キーが押されたときにkeyDownHandlerを呼び出す
document.addEventListener('keydown', keyDownHandler);
// キーが離されたときにkeyUpHandlerを呼び出す
document.addEventListener('keyup', keyUpHandler);

関数|キーが押された/離されたを示す - keyDownHandler/keyUpHandler

では、関数を定義していきましょう。keyDownHandlerkeyUpHandler には引数 e を指定し、「押された」または「離された」キーの情報を受け取れるようにします。

  • e が持つ情報の中から、key プロパティで「押された」または「離された」キーの値を取得し、 switch 文でチェックします。
    • 押されたキーに一致する変数の値を true にし、そのキーが押されたことを示します。
    • 離されたキーに一致する変数の値を false にし、そのキーが離されたことを示します。
// そのキーが押されたことを示す
function keyDownHandler(e) {
  switch (e.key) {
    case 'w':
      wPressed = true;
      break;
    case 's':
      sPressed = true;
      break;
  }
}

// そのキーが離されたことを示す
function keyUpHandler(e) {
  switch (e.key) {
    case 'w':
      wPressed = false;
      break;
    case 's':
      sPressed = false;
      break;
  }
}

以上で、パドル操作用の w キーまたは s キーが「押された」または「離された」ことを確認する関数を定義できました。

関数 keyDownHandlerkeyUpHandler については以下の記事で詳しく解説しているので参考にしてください。

JavaScript - 矢印キーで動かす方法
HTML のキャンバス <canvas> に描いたオブジェクトを JavaScript で動かすコードを解説します。ゲームでプレイヤーを操作するように、キーを押してオブジェクトを移動させる方法を学びましょう。

関数|パドルの位置を更新 - updatePaddle

w キーまたは s キーが「押された」か「離された」かを確認できるようになったら、その情報を使ってパドルを動かせるようにしましょう。パドルを動かすためには、パドルの描画位置を更新するので、updatePaddle という名前をつけて関数を定義していきます。

// パドルの位置を更新
function updatePaddle() {

  // 左パドルを動かす
  // 右パドルを動かす

}

左パドルを動かす

まずは、左パドルです。w キーが押されている、または、s キーが押されていることを if...else 文でチェックします。さらに、論理積演算子 && で、パドルの上端や下端がキャンバス内に収まっていることも条件に追加します。これは、キーを押し続けてパドルがキャンバスの外側まで動いて行ってしまうのを防ぐためです。

左パドルを上へ動かすコードから見てみましょう。

  • w キーが押されていて、かつ、パドルがキャンバスの上端より内側にあることを条件とします。
  • パドルの描画位置 leftPaddleY から paddleVelocity をマイナスし、上へ動かします。
function updatePaddle() {

  // 左パドルを動かす
  // 上へ動かす
  if (wPressed && leftPaddleY > 0) {
    leftPaddleY -= paddleVelocity;

  } else if () {
    // 下へ動かす
    // …
  }

  // 右パドルを動かす
  // …
  
}

続いて、左パドルを下へ動かすコードです。else if の部分に書いていきますよ。

  • s キーが押されていて、かつ、パドルがキャンバスの下端より内側にあることを条件とします。
  • パドルの描画位置 leftPaddleYpaddleVelocity をプラスし、下へ動かします。
function updatePaddle() {

  // 左パドルを動かす
  if (wPressed && leftPaddleY > 0) {
    leftPaddleY -= paddleVelocity;

     // 下へ動かす
  } else if (sPressed && leftPaddleY + paddleHeight < canvas.height)  {
    leftPaddleY += paddleVelocity;
  }

  // 右パドルを動かす
  // …
  
}

右パドルを動かす

次は、右パドルです。右パドルは自動で動かすので、左パドルのコードとは考え方が異なります。

  • コンピューターの勝負強さを、変数 computerLevel で設定します。この値を大きくするとパドルが早く動くので、打ち返してくる確率が高くなります。
  • ボールと右パドルの位置関係から数値を割り出し、パドルを上または下へ動かします。
  • パドルがキャンバスの上下からはみ出ないように、位置を指定します。
function updatePaddle() {
  if (wPressed && leftPaddleY > 0) {
    leftPaddleY -= paddleVelocity;
  } else if (sPressed && leftPaddleY + paddleHeight < canvas.height)  {
    leftPaddleY += paddleVelocity;
  }

  // 右パドルを動かす
  // コンピューターの強さ
  let computerLevel = 0.06;

  // 右パドルを上または下へ動かす
  rightPaddleY += (ballY - (rightPaddleY + paddleHeight / 2)) * computerLevel;

  // 上の限界位置
  if (rightPaddleY < 0) {
    rightPaddleY  = 0;
  }
  // 下の限界位置
  if (rightPaddleY + paddleHeight > canvas.height) {
    rightPaddleY  = canvas.height - paddleHeight;
  }

}

以上で、左右のパドルの描画位置を更新して動かすための関数 updatePaddle を定義できました。

押されているキーの方向へオブジェクトを動かす方法は、こちらで詳しく解説しているのでぜひご覧ください。

Part 1 で解説した全コード

ピンポンゲームの作り方 Part 1 はここまでとなります。解説してきた全コードを下にまとめました。

// キャンバスに描画する準備
const canvas = document.querySelector('#game-canvas');
const ctx = canvas.getContext('2d');

//=== 変数 ===================================
// パドル
const paddleWidth = 10;
const paddleHeight = 100;
let leftPaddleY = canvas.height / 2 - paddleHeight / 2;
const leftPaddleX = 30;
let rightPaddleY = canvas.height / 2 - paddleHeight / 2;
const rightPaddleX = canvas.width - 40;
const paddleVelocity = 5;
// ゲーム中かどうかどうか(trueまたはfalse)
let playing = false;
// キーが押されているかどうか(trueまたはfalse)
let wPressed = false;
let sPressed = false;

//=== 関数 ===================================
// Enterキーの押下でゲーム開始
function startGame(e) {
  if (e.key === 'Enter') {
    if (!playing) {
      gameLoop();
      playing = true;
    }
  }
}

// そのキーが押されたことを示す
function keyDownHandler(e) {
  switch (e.key) {
    case 'w':
      wPressed = true;
      break;
    case 's':
      sPressed = true;
      break;
  }
}

// そのキーが離されたことを示す
function keyUpHandler(e) {
  switch (e.key) {
    case 'w':
      wPressed = false;
      break;
    case 's':
      sPressed = false;
      break;
  }
}

// パドルの位置を更新
function updatePaddle() {
  //----- 左パドル(ユーザー操作) ---------
  if (wPressed && leftPaddleY > 0) {
    leftPaddleY -= paddleVelocity;
  } else if (sPressed && leftPaddleY + paddleHeight < canvas.height)  {
    leftPaddleY += paddleVelocity;
  }
  //----- 右パドル(自動) ---------------
  let computerLevel = 0.06;
  rightPaddleY += (ballY - (rightPaddleY + paddleHeight / 2)) * computerLevel;
  if (rightPaddleY < 0) {
    rightPaddleY  = 0;
  }
  if (rightPaddleY + paddleHeight > canvas.height) {
    rightPaddleY  = canvas.height - paddleHeight;
  }
}

//=== イベント ===================================
document.addEventListener('keydown', startGame);
document.addEventListener('keydown', keyDownHandler);
document.addEventListener('keyup', keyUpHandler);

現段階では、キャンバスには何も表示されません。上のコードでは、「キーが押されたとき、ゲームを開始する。パドルの描画位置を更新する。」などの指示ができるようになっただけで、実際にキャンバスに「描画する」処理はまだ書いていないからです。

最後に、下の CodePen で実際に遊んでみてください。

See the Pen JavaScript - Pong Game by Pyxofy (@pyxofy) on CodePen.

Pyxofy (著)「きょうからはじめるスクラッチプログラミング入門」

Pyxofy が Scratch の電子書籍を出版しました!Kindle・Apple Books からご購入ください。

詳細はこちら

まとめ

今回は、JavaScript で作るピンポンゲームのコードのうち、キーで操作する部分とパドルについて解説しました。

キーで操作するためには、キーが押されたことと離されたことをチェックできるようにして、押されているときだけ処理を実行するようにします。また、パドルを動かすときは、キャンバスに対してパドルがどの位置にあるのかを確認しながら座標を変更し、パドルがキャンバスからはみ出さないようにしました。

今後、ボールの動きについては Part 2 で紹介し、ゲームの仕上げは Part 3 で行います。引き続き、ピンポンゲームを一緒に作成していきましょう。

最後まで読んでいただき、ありがとうございます。この記事をシェアしてくれると嬉しいです!

SNSで Pyxofy とつながりましょう! LinkedInThreadsMastodon X (Twitter) @pyxofyFacebook

関連記事

JavaScript - イベントオブジェクト - function(e) の e とは?
function(e) の e はイベントオブジェクトを受け取るための変数です。イベントオブジェクトにアクセスして、押されたキーの種類やマウスポインターの座標などの情報を取得できるようになりましょう。
CSS Art – How to Make a Game Character – Kirby
Pink all over and packed with powerful moves. Unforgettable starry eyes. Join us in creating the iconic Kirby in this article, step-by-step.
CSS Animation – @property Radial Gradient Zoom Animation
Zoom in and Zooming out. Zoom animation effects are fun! In this step-by-step article, you’ll learn how to make them with CSS.
スクラッチプログラミング - やじるしキーでスプライトをうごかそう
キーボードをつかってスプライトをうごかすためには、キーがおされたかどうかをしらべるブロックをつかいます。上下左右の「やじるしキー」で、スプライトをそうさできるようになりましょう。