【JavaScript講座】タイピングゲームを作成する No.5

タイピングゲームを作成する

JavaScript講座、タイピングゲームを作成する No.5 です。

前回の記事を確認したい方は下記からどうぞ。

【JavaScript講座】タイピングゲームを作成する No.4

2019.10.12

今回で一応完成させようと思っています。



タイピングゲームを作成する

それでは、タイピングゲームを作成します。

今回の記事のポイントは以下の通りです。

  • プレイ中にタイマーがリセットされないようにする
  • リプレイできるようにする
  • ゲームが終了したときに成績を表示させる

それでは見ていきましょう。

プレイ中にタイマーがリセットされないようにする

プレイ中にタイマーがリセットされないようにします。

前回のサンプルコードだとプレイ中でもイベントが発生してしまうので、そこを修正していきます。

まずは、ゲームが開始したかどうかのフラグを変数として用意します。

let startFlg = false;

初期値は false を格納しておきます。

そして、クリックしたときに発動するイベントを下記のように書き換えます。

window.addEventListener('click', () => {
  if (startFlg === true) {
    return;
  }

  startFlg = true;

  typingWord.textContent = word;
  startTime = Date.now();
  updateTimeLimit();
});

クリックして、startFlg の値が true なら、return文によって何も起こらず終了します。

false なら、startFlg に true が代入され、タイマーが発動するようになっています。

あとは、Game Over になったときに startFlg の値を false にしたいので、そこも修正します。

function updateTimeLimit() {
  const timer = startTime + timeLimit - Date.now();
  timeLeftLabel.textContent = (timer / 1000).toFixed(2);

  const timeoutId = setTimeout(() => {
    updateTimeLimit();
  }, 10);

  if (timer < 0) {
    startFlg = false;
    clearTimeout(timeoutId);
    timeLeftLabel.textContent = '0.00';
    setTimeout(() => {
      alert('Game Over');
    }, 100);
  }
}

あと、keydown のイベントも修正します。

下記のように入れてやれば、false の場合、return文により、終了します。

if (startFlg === false) {
  return;
}

リプレイできるようにする

次はリプレイできるようにします。

コードに関しては、あとで確認してもらうことにして、説明だけしていきます。

タイマーが終了した段階で下記のように Click to replay を代入して、表示させます。

typingWord.textContent = 'Click to replay';

あとは、クリックしたときのイベントで word、loc、wordCount、typeMiss を初期化するようにしてあげれば良いかと思います。

ゲームが終了したときに成績を表示させる

最後にゲームが終了したときに成績を表示させるようにしてあげます。

正答率と入力速度を格納する変数を宣言し、ゲームが終了した段階で計算して表示させます。

入力文字数とタイプミスを表示させる仕組みと同じなんで、詳しく説明はしません。

次のサンプルコードをご確認ください。

今回の記事のサンプルコード

今回の記事のサンプルコードです。

最終なんで、HTML と JavaScript どちらも紹介します。

まずは、HTML から。

<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>ぱそたのタイピング道場</title>
  <link rel="stylesheet" href="css/style.css"
</head>
<body>
  <h1>My Typing Game</h1>
  <hr>
  <p id="typingWord">
  Please click
  </p>
  <hr>
  <p id="info">
  入力文字数:<span id="wordCount">0</span>
  タイプミス:<span id="typeMiss">0</span>
  正答率:<span id="accuracy">0</span>
  入力速度(WPM):<span id="typeSpeed">0</span>
  残り時間:<span id="timeLeft">0</span>
  </p>
  <script src="js/main.js"></script>
</body>
</html>

続いて、JavaScript です。

use strict 宣言だけ先頭に追記してください。

const words = [
  'Thomas',
  'Edward',
  'Henry',
  'Gordon',
  'James',
  'Percy',
  'Toby',
];
let word;
let loc;
let wordCount;
let typeMiss;
let accuracy;
let typeSpeed;
const timeLimit = 30 * 1000;
let startTime;
let startFlg = false;

const typingWord = document.getElementById('typingWord');
const wordCountLabel = document.getElementById('wordCount');
const typeMissLabel = document.getElementById('typeMiss');
const timeLeftLabel = document.getElementById('timeLeft');
const accuracyLabel = document.getElementById('accuracy');
const typeSpeedLabel = document.getElementById('typeSpeed');

function updateTypingWord() {
  let update = '';
  for (let i = 0; i < loc; i++){
    update += '-';
  }
  typingWord.textContent = update + word.substring(loc);
}

function updateTimeLimit() {
  const timer = startTime + timeLimit - Date.now();
  timeLeftLabel.textContent = (timer / 1000).toFixed(2);

  const timeoutId = setTimeout(() => {
    updateTimeLimit();
  }, 10);

  if (timer < 0) {
    startFlg = false;
    clearTimeout(timeoutId);
    timeLeftLabel.textContent = '0.00';
    setTimeout(() => {
      alert('Game Over');
      accuracy = wordCount + typeMiss === 0 ? 0 : wordCount / ( wordCount + typeMiss ) * 100;
      typeSpeed = wordCount / ( timeLimit / 60000 );
      accuracyLabel.textContent = accuracy.toFixed(2);
      typeSpeedLabel.textContent = typeSpeed;
    }, 100);

    typingWord.textContent = 'Click to replay';
  }
}

window.addEventListener('click', () => {
  if (startFlg === true) {
    return;
  }

  startFlg = true;
  loc = 0;
  wordCount = 0;
  typeMiss = 0;
  accuracy = 0;
  typeSpeed = 0;

  wordCountLabel.textContent = wordCount;
  typeMissLabel.textContent = typeMiss;
  accuracyLabel.textContent = accuracy;
  typeSpeedLabel.textContent = typeSpeed;
  word = words[Math.floor(Math.random() * words.length)];

  typingWord.textContent = word;
  startTime = Date.now();
  updateTimeLimit();
});

window.addEventListener('keydown', e => {
  if (startFlg === false) {
    return;
  }
  if (e.key === word[loc]){
    loc++;
    if (loc === word.length) {
      word = words[Math.floor(Math.random() * words.length)];
      loc = 0;
    }
    updateTypingWord();
    wordCount++;
    wordCountLabel.textContent = wordCount;
  } else if (e.key === 'Shift') {
    ;
  } else {
    typeMiss++;
    typeMissLabel.textContent = typeMiss;
  }
});
タイピングゲーム

最後に

いかがでしょうか。

今回でタイピングゲーム制作は終了です。

非常にシンプルなものですが、配列部分のワードを自分の好みのものに変えて練習すると使えないことはないと思います。

あとからごちゃごちゃ修正したんで、あまりきれいなコードではありませんが、ご了承下さい。