独り言

プログラミングの講師をしています。新人研修で扱う技術の解説と個人の技術メモ、技術書の紹介など

【JavaScript】タイピンゲーム2

以前JavaScriptでのタイピングゲーム1のソースコードを載せましたが、少し改良したのでそちらも載せます。
改善点としては、

  • リアルタイムで時間が表示される
  • ストップボタンを追加
  • 出題される単語のテーマを選べるように

といった感じです。
こちらもJavaScriptの学習やゲーム作成の参考にどうぞ。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>タイピングゲーム</title>
    <style>
        body {
            text-align: center;
        }
        #target {
            font-size: 48px;
        }
        .btn, #time, #count {
            font-size: 18px;
        }
    </style>
</head>
<body>
    <h1>タイピングゲーム</h1>
    <p>単語テーマ
        <select id="word-theme" name="word-theme">
            <option value="color">カラー</option>
            <option value="fruits">フルーツ</option>
        </select>
    </p>
    <p>
        <button class="btn" type="button" id="start-btn" name="start-btn">スタート<br>(Enter)</button>
        <button class="btn" type="button" id="stop-btn" name="stop-btn">ストップ<br>(ESC)</button>
    </p>
    <p id="target"></p>
    <p id="time"></p>
    <p id="count"></p>
    <script>
        'use strict';

        const target = document.getElementById('target');
        const time = document.getElementById('time');
        const count = document.getElementById('count');
        const startBtn = document.getElementById('start-btn');
        const stopBtn = document.getElementById('stop-btn');
        startBtn.disabled = false;
        stopBtn.disabled = true;

        let words = [];
        let wordCount = 0; // 完了した文字数
        let word = ''  // 現在入力中の文字
        let index = 0; // 文字の現在地
        let missCount = 0;
        let startTime = 0;
        let setTimeoutId = 0;

        function dispTime() {
            time.textContent = ((new Date - startTime) / 1000).toFixed(2) + '秒';
            setTimeoutId = setTimeout(() => {
                dispTime();
            }, 50);
        }

        // ゲームスタート時の処理
        function gameStart() {
            startBtn.disabled = true;
            stopBtn.disabled = false;
            const wordTheme = document.getElementById('word-theme').value;
            switch(wordTheme) {
                case 'color':
                    words = ['red', 'blue', 'green', 'yellow', 'pink', 'black', 'white', 'purple', 'gray', 'orange'];
                    break;
                case 'fruits':
                    words = ['apple', 'banana', 'grape', 'peach', 'orange', 'pear', 'melon', 'lemon', 'strawberry', 'mango'];
                    break;
                default :
                    words = ['abcde'];
                    break;
            }

            wordCount = words.length;
            missCount = 0;
            count.textContent = `残り ${wordCount} つ`;
            startTime = new Date();
            word = words.splice(Math.floor(Math.random() * words.length), 1).join();
            target.textContent = word;
            dispTime();
        }

        // ゲームストップ
        function gameStop() {
            clearTimeout(setTimeoutId);
            // target.textContent = '';
            // time.textContent = '';
            // count.textContent = '';
            startBtn.disabled = false;
            stopBtn.disabled = true;
        }

        // ボタン押下時
        document.getElementById('start-btn').addEventListener('click', gameStart, false);
        document.getElementById('stop-btn').addEventListener('click', gameStop, false);

        // キー押下時
        document.addEventListener('keydown', e => {
            // Enter押下時はスタート
            if(e.key === 'Enter' && startBtn.disabled === false) {
                gameStart();
                return;
            }
            // ESCキー押下でストップ
            if(e.key === 'Escape' && stopBtn.disabled === false) {
                gameStop();
                return;
            }

            if(stopBtn.disabled === true) {
                return;
            }

            if(word.charAt(index) === e.key)  {
                index++;
                target.textContent = '_'.repeat(index) + word.substring(index);

                if(index === word.length) {
                    // 1単語終了時
                    index = 0;
                    wordCount--;
                    count.textContent = `残り ${wordCount} `;

                    if(wordCount === 0) {
                        // ゲーム終了時
                        clearTimeout(setTimeoutId);
                        count.textContent = `タイプミス ${missCount}個`;
                        startBtn.disabled = false;
                        stopBtn.disabled = true;
                    } else {
                        word = words.splice(Math.floor(Math.random() * words.length), 1).join();
                        target.textContent = word;
                    }
                }
            } else {
                missCount++;
            }  

        });

    </script>
</body>
</html>