LoginSignup
0
0

【JavaScript】日本語のローマ字の複数の入力判定ができるタイピングゲームのライブラリと簡単な使用例【typing-jp】

Last updated at Posted at 2024-05-25

前書き

タイピングゲームのライブラリを作ったのでタイピングゲームを作りたいと思う。

成果物

雑な設計

・日本語のテキストとローマ字を表示する。
・入力に応じてローマ字の色を変える。
・複数のローマ字入力に対応する。
・ミス数、タイムを計測する。

作る

ライブラリの読み込み

ありがたいことにCDNが使えるので使わせてもらう。
(皆さんは最新バージョンを使ってください!)

<script src="https://cdn.jsdelivr.net/npm/@mogamoga1024/typing-jp@1.0.2/dist/cdn/typing-jp.js"></script>

問題

問題は適当にChatGPT君に考えてもらう。

表示用の文字列とTypingTextオブジェクトをオブジェクトに突っ込んで配列に格納する。

question.js
const allQuestionList = [
    { text: "りんご", typingText: new TypingText("りんご") },
    { text: "バナナ", typingText: new TypingText("バナナ") },
    { text: "オレンジ", typingText: new TypingText("オレンジ") },
    { text: "いちご", typingText: new TypingText("いちご") },
    { text: "ブドウ", typingText: new TypingText("ブドウ") },
    { text: "パイナップル", typingText: new TypingText("パイナップル") },
    { text: "みかん", typingText: new TypingText("みかん") },
    { text: "レモン", typingText: new TypingText("レモン") },
    { text: "メロン", typingText: new TypingText("メロン") },
    { text: "スイカ", typingText: new TypingText("スイカ") },
    { text: "トマト", typingText: new TypingText("トマト") },
    { text: "きゅうり", typingText: new TypingText("きゅうり") },
    { text: "キャベツ", typingText: new TypingText("キャベツ") },
    { text: "レタス", typingText: new TypingText("レタス") },
    { text: "ほうれん草", typingText: new TypingText("ほうれんそう") },
    { text: "かぼちゃ", typingText: new TypingText("かぼちゃ") },
    { text: "じゃがいも", typingText: new TypingText("じゃがいも") },
    { text: "さつまいも", typingText: new TypingText("さつまいも") },
    { text: "玉ねぎ", typingText: new TypingText("たまねぎ") },
    { text: "にんじん", typingText: new TypingText("にんじん") },
    { text: "大根", typingText: new TypingText("だいこん") },
    { text: "しいたけ", typingText: new TypingText("しいたけ") },
    { text: "まいたけ", typingText: new TypingText("まいたけ") },
    { text: "しめじ", typingText: new TypingText("しめじ") },
    { text: "えのき", typingText: new TypingText("えのき") },
    { text: "なす", typingText: new TypingText("なす") },
    { text: "ピーマン", typingText: new TypingText("ピーマン") },
    { text: "トウモロコシ", typingText: new TypingText("トウモロコシ") },
    { text: "ブロッコリー", typingText: new TypingText("ブロッコリー") },
    { text: "カリフラワー", typingText: new TypingText("カリフラワー") },
    { text: "もやし", typingText: new TypingText("もやし") },
    { text: "にら", typingText: new TypingText("にら") },
    { text: "ねぎ", typingText: new TypingText("ねぎ") },
    { text: "セロリ", typingText: new TypingText("セロリ") },
    { text: "パセリ", typingText: new TypingText("パセリ") },
    { text: "アスパラガス", typingText: new TypingText("アスパラガス") },
    { text: "オクラ", typingText: new TypingText("オクラ") },
    { text: "ズッキーニ", typingText: new TypingText("ズッキーニ") },
    { text: "ごぼう", typingText: new TypingText("ごぼう") },
    { text: "れんこん", typingText: new TypingText("れんこん") },
    { text: "枝豆", typingText: new TypingText("えだまめ") },
    { text: "小松菜", typingText: new TypingText("こまつな") },
    { text: "三つ葉", typingText: new TypingText("みつば") },
    { text: "春菊", typingText: new TypingText("しゅんぎく") },
    { text: "山芋", typingText: new TypingText("やまいも") },
    { text: "とうがん", typingText: new TypingText("とうがん") },
    { text: "鯖(サバ)", typingText: new TypingText("さば") },
    { text: "鰯(イワシ)", typingText: new TypingText("いわし") },
    { text: "鮭(サケ)", typingText: new TypingText("さけ") },
    { text: "鯛(タイ)", typingText: new TypingText("たい") },
    { text: "ブリ", typingText: new TypingText("ブリ") },
    { text: "タコ", typingText: new TypingText("タコ") },
    { text: "イカ", typingText: new TypingText("イカ") },
    { text: "アサリ", typingText: new TypingText("アサリ") },
    { text: "ハマグリ", typingText: new TypingText("ハマグリ") },
    { text: "シジミ", typingText: new TypingText("シジミ") },
    { text: "カニ", typingText: new TypingText("カニ") },
    { text: "エビ", typingText: new TypingText("エビ") },
    { text: "ウニ", typingText: new TypingText("ウニ") },
    { text: "カキ", typingText: new TypingText("カキ") },
    { text: "ホタテ", typingText: new TypingText("ホタテ") },
    { text: "鶏肉", typingText: new TypingText("とりにく") },
    { text: "牛肉", typingText: new TypingText("ぎゅうにく") },
    { text: "豚肉", typingText: new TypingText("ぶたにく") },
    { text: "羊肉", typingText: new TypingText("ようにく") },
    { text: "馬肉", typingText: new TypingText("ばにく") },
    { text: "鹿肉", typingText: new TypingText("しかにく") },
    { text: "カモ肉", typingText: new TypingText("カモにく") },
    { text: "鶉(ウズラ)", typingText: new TypingText("うずら") },
    { text: "納豆", typingText: new TypingText("なっとう") },
    { text: "うなぎ", typingText: new TypingText("うなぎ") },
    { text: "たらこ", typingText: new TypingText("たらこ") },
    { text: "いくら", typingText: new TypingText("いくら") },
    { text: "数の子", typingText: new TypingText("かずのこ") },
    { text: "筍(タケノコ)", typingText: new TypingText("たけのこ") },
    { text: "ワカメ", typingText: new TypingText("ワカメ") },
    { text: "昆布", typingText: new TypingText("こんぶ") },
    { text: "のり", typingText: new TypingText("のり") },
    { text: "ひじき", typingText: new TypingText("ひじき") },
    { text: "サバ缶", typingText: new TypingText("サバかん") },
    { text: "ツナ缶", typingText: new TypingText("ツナかん") },
    { text: "うずらの卵", typingText: new TypingText("うずらのたまご") },
    { text: "鳥の唐揚げ", typingText: new TypingText("とりのからあげ") },
    { text: "天ぷら", typingText: new TypingText("てんぷら") },
    { text: "すき焼き", typingText: new TypingText("すきやき") },
    { text: "鍋物", typingText: new TypingText("なべもの") },
    { text: "味噌汁", typingText: new TypingText("みそしる") },
    { text: "煮物", typingText: new TypingText("にもの") },
    { text: "焼き魚", typingText: new TypingText("やきざかな") },
    { text: "焼肉", typingText: new TypingText("やきにく") },
    { text: "牛丼", typingText: new TypingText("ぎゅうどん") },
    { text: "寿司", typingText: new TypingText("すし") },
    { text: "さしみ", typingText: new TypingText("さしみ") },
    { text: "ラーメン", typingText: new TypingText("ラーメン") },
    { text: "うどん", typingText: new TypingText("うどん") },
    { text: "そば", typingText: new TypingText("そば") },
    { text: "パスタ", typingText: new TypingText("パスタ") },
    { text: "カレーライス", typingText: new TypingText("カレーライス") },
    { text: "ピザ", typingText: new TypingText("ピザ") },
    { text: "ハンバーガー", typingText: new TypingText("ハンバーガー") },
];

function createRandomQuestionList(count) {
    const tmpQuestionList = [...allQuestionList];
    const randomQuestionList = [];

    for (let i = 0; i < count; i++) {
        const randomIndex = Math.floor(Math.random() * tmpQuestionList.length);
        const question = tmpQuestionList.splice(randomIndex, 1)[0];
        randomQuestionList.push(question);
    }
    
    return randomQuestionList;
}

タイピングの処理

100行程度のコードなんで特に言うことなし。
gameMain関数だけ読めばいいです。

const domText = document.querySelector("#text");
const domRomanContainer = document.querySelector("#roman-container");
const domRoman1 = document.querySelector("#roman1");
const domRoman2 = document.querySelector("#roman2");
const domResult = document.querySelector("#result");

domResult.style.display = "none";

let questionList = createRandomQuestionList(10);
let questionIndex = 0;
let typingText = questionList[questionIndex].typingText;

let missCount = 0;
let typeCount = 0;
let startTime = 0;
let elapsedTime = 0;

gameStart();

function gameStart() {
    domText.innerText = "Spaceで開始";

    window.onkeydown = e => {
        if (e.key === " ") {
            gameMain();
        }
    }
}

function gameMain() {
    domText.innerText = `${questionIndex + 1}:${questionList[questionIndex].text}`;
    domRoman2.innerText = typingText.remainingRoman;

    startTime = performance.now();

    window.onkeydown = e => {
        if (e.repeat) {
            return;
        }
    
        // ShiftやF11のような入力を弾く
        if (!TypingText.isValidInputKey(e.key)) {
            return;
        }

        typeCount++;
    
        const isCapsLock = e.getModifierState("CapsLock");
    
        // キー入力の更新
        const result = typingText.inputKey(e.key, isCapsLock);
        
        switch (result) {
            // 不一致の場合
            case "unmatch":
                missCount++;
                return;
    
            // 一致しているが文章が未完成の場合
            case "incomplete":
                domRoman1.innerText += e.key;
                domRoman2.innerText = typingText.remainingRoman;
                return;
    
            // 文章が完成した場合
            case "complete":
                // クリアしたか
                if (++questionIndex >= questionList.length) {
                    elapsedTime = performance.now() - startTime;
                    domRoman1.innerText += e.key;
                    domRoman2.innerText = "";
                    gameEnd();
                    return;
                }
    
                // 次の文章へ
                domText.innerText = `${questionIndex + 1}:${questionList[questionIndex].text}`;
                typingText = questionList[questionIndex].typingText;
            
                domRoman1.innerText = "";
                domRoman2.innerText = typingText.remainingRoman;
    
                return;
        }
    }
}

function floor(num, decimalPlaces = 0) {
    const factor = Math.pow(10, decimalPlaces);
    return Math.floor(num * factor) / factor;
}

function gameEnd() {
    window.onkeydown = null;

    domText.innerText = "リザルト";
    domRomanContainer.style.display = "none";
    domResult.style.display = "";

    const epm = missCount / (elapsedTime / (1000 * 60));
    const kpm = typeCount / (elapsedTime / (1000 * 60));

    domResult.innerText  = `スコア:${floor(kpm * Math.pow(1 - missCount / typeCount, 3), 2)}\n`;
    domResult.innerText += `クリアタイム:${floor(elapsedTime / 1000, 2)}秒\n`;
    domResult.innerText += `誤タイプ率:${floor(missCount / typeCount * 100, 2)}%\n`;
    domResult.innerText += `1分毎のタイプ数:${floor(kpm, 2)}\n`;
    domResult.innerText += `1分毎の誤タイプ数:${floor(epm, 2)}\n`;
}
HTML & CSS
index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>タイピングゲーム</title>
  <link rel="stylesheet" href="style.css">
  <script src="https://cdn.jsdelivr.net/npm/@mogamoga1024/typing-jp@1.0.5/dist/cdn/typing-jp.js"></script>
</head>
<body>
  <div id="text"></div>
  <div id="roman-container">
    <span id="roman1"></span><span id="roman2"></span>
  </div>
  <div id="result"></div>

  <script src="question.js"></script>
  <script src="main.js"></script>
</body>
</html>
style.css
* {
  font-variant-ligatures: none;
}

#text {
  font-size: 2rem;
}

#roman-container, #result {
  font-size: 1.5rem;
}

#roman1 {
  color: red;
}

最後に

使ってね~☆

ライブラリを作ろうと思ったきっかけの記事

昔書いたやつ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0