※この記事は「2021年4月15日」に更新しました。
JavaScript講座、タイピングゲームを作成する No.5 です。
前回の記事を確認したい方は下記からどうぞ。
今回で、完成させようと思っています。

タイピングゲームを作成する
それでは、タイピングゲームを作成します。
今回の記事のポイントは、以下の通りです。
- プレイ中にタイマーがリセットされないようにする
- リプレイできるようにする
- ゲームが終了したときに成績を表示させる
順番に解説していきます。
プレイ中にタイマーがリセットされないようにする
プレイ中にタイマーがリセットされないようにします。
前回のサンプルコードだと、プレイ中でもイベントが発生してしまうので、そこを修正していきます。
まずは、ゲームが開始したかどうかのフラグを変数として用意します。
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 を初期化するようにしてあげれば良いかと思います。
ゲームが終了したときに成績を表示させる
最後に、ゲームが終了したときに成績を表示させるようにしてあげます。
正答率と入力速度を格納する変数を宣言し、ゲームが終了した段階で計算して表示させます。
入力文字数とタイプミスを表示させる仕組みと同じなんで、詳しく説明はしません。
次のサンプルコードをご確認ください。
サンプルコード【JavaScript】
サンプルコードです。
最終なんで、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;
}
});
最後に
いかがでしょうか。
今回で、タイピングゲーム制作は終了です。
非常にシンプルなものですが、配列部分のワードを自分の好みのものに変えて練習すると、使えないことはないと思います。
あとからごちゃごちゃ修正したんで、あまりきれいなコードではありませんが、ご了承下さい。

