0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

タイピングゲーム

Last updated at Posted at 2020-12-11

今回はタイピングゲームを作成

今回のあらすじとシナリオはこちら
Untitled Diagram-Page-1.png

Diagram-Page-2 (2).png

##ランダムでお題を選び出力する
次はあらかじめお題を作っておき、それを出題させる処理

お題は配列に入れておく
記事を参考に

const messages = ['soccer','baseball','basletball'];

const messageNo = Math.floor( Math.random() * messages.length);
console.log(messages[messageNo]);

でランダムで出力されるようになった

https://techacademy.jp/magazine/25665

##まずキー入力の取得について考える
調べていたら先人様がわかりやすく纏めてくれたqiitaがあったのでそちらなどを参考にして今回はevent.codeを使ってみたいと思う。
MDNを参考にして押したキーボードをconsoleで出力させた

window.addEventListener("keydown", function(event) {
  let str = event.key
            event.code + "'";
 console.log(str)
}, true);

これで出力をさせる事が出来た

mdn https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent/code

##入力した文字があっているかを確認する
恐らくここからが問題

if文で比較して合っていたらconsoleにtrueを返したい。

まずはconsoleで文字列を比較する代わりに変数を比較したい。
まだ一文字は出力できていないから、仮でsoccerにサッカーを入れてみた。

const soccer ="socceer"
console.log( soccer == messages[messageNo] );

これで問題なくtrue,falseが出力された

if文の条件分岐の同じものを入れてみる

if(soccer == messages[messageNo]) {
    console.log("true");
}
else {
    console.log('false');
}

こちらも問題なかった

https://www.sejuku.net/blog/21413

##文字列を一文字ずつ取得する
お題を一文字ずつ取得してキー入力と正誤判断をする。

記事を参考にランダムでお題の頭文字をとってみる

console.log(messages[messageNo].charAt(0));

一文字ずつ取得するのはこれで問題ない

https://itsakura.com/javascript-charat

今は0だが入力したものが合っていたら次の文字に移れるようにしたい。

思いついた方法は
・for文でぶん回す
・if文でtrueが返ってきたら0+1をする

まずif文で書いてみたいと思う。
どうやって0を1にするか
charAt(0)を変数にしてそこに+1をしていってみる

const num = 0
    if(str == messages[messageNo].charAt(num)) {
        console.log("true");
        const num = num + 1
}
else {
    console.log('false');
}

やってみたがCannot access 'num' before initializationとエラー文が返ってきた。

MDNの記事を参考にif文の中をnum =num +1に修正したら違うエラー文が出た。
Assignment to constant variable.

これについてのMDNの記事を読んだところ変数の宣言にconstを使ったミスだった。constは再代入・再宣言が出来ないので、再代入が出来るvarにしたらエラー文は出なくなった。しかし文字が変わらないのでconsoloでnumを出力してみると、ずっと1のままだった。

if文だけでのループ処理見たいのはやはり厳しいのか

if文を関数にぶち込めば?
strが関数スコープに存在しているのでアクセスすることが出来ない。
どうしたら使えるか?

もう一度立ち戻って検証した結果、関数スコープにnumを定義していたのが問題だった。

動かなったコードはこっち

window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
    var num = 0
    if(str == messages[messageNo].charAt(num)) {
      // console.log("true");
        num = num+ 1
        console.log(num)
}
else {
    console.log('false');
}
}, 
true);

正常に動いたのがこっち

    var num =0
window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
  
    // var num =0
    if(str == messages[messageNo].charAt(num)) {
        // num = num + 1
         // console.log("true");
         num++;
         console.log(num)
    }
           else {
           console.log('false');
        };
    
  },
true);

今考えればキー入力をした時に毎回var num = 0を定義してたらカウントされないわ、、、

今回ミスをしたので次回からは間違えないことを心に決めて次に行く
(やっぱ思った通り動くと嬉しい)

##単語が終わって次の問題をセット
最後の文字列の文字を打ち終わったらお題を切り替える必要がある。
charAtに最後の文字列を取得できる便利なものはあるか、、、

console.log(str.charAt(str.length - 1)); // 0
あった!!!

http://cly7796.net/wp/javascript/extract-a-string/
置き換えて出力してみる

無事出力完了!!!

console.log(messages[messageNo].charAt(messages[messageNo].length - 1)); 
///baseballの時は「l」が返ってきた

これを正誤判定するif文に付け加えたいと思う。

window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
  
    // var num =0
    if(str == messages[messageNo].charAt(num)) {
        // num = num + 1
         // console.log("true");
         num++;
         console.log(num)
    } else if(str == messages[messageNo].charAt(messages[messageNo].length - 1)) {
    console.log("終わり")
    }else {
           console.log('false');
      };
},
  true);

これで最後の文字を打ち込んだ時に「終わり」と返したかったが、出来なかった。
soccerの場合は最後に「r」を二回打ち込まないと「終わり」は帰ってこなかった、、、

恐らく一番上の条件に吸収されているのだと思う。(言い方があっているかわからないが)
だからその中に書いてみる

if(str == messages[messageNo].charAt(num)) {
        // num = num + 1
         // console.log("true");
         num++;
      console.log(num)
      if (str == messages[messageNo].charAt(messages[messageNo].length - 1)) {
        console.log("終わり")
      }
    // } else if(str == messages[messageNo].charAt(messages[messageNo].length - 1)) {
    //   // var messageNo = Math.floor( Math.random() * messages.length);
    //   console.log("おわり")
    }else {
           console.log('false');
      };

これで無事に返ってきた。
しかしbaseballやbasketballなど最後に「l」が二回続くと最初のlと後のl両方で「終わり」が返ってきてしまう。
同じ文字では判別できないので文字の数で判断することにする。
こちらの記事を参考にして書いてみた。

var array=[]
window.addEventListener("keydown", function (event) {
  let str = event.key
  array.push(event.key);
  let strnum = array.join("")
///以下省略
}

if文の条件をstrnum.length == messages[messageNo].length)をこっちにした所無事終了させることが出来た。
後は終了したら文字列を消去するのとタイピングを間違えた時にそのキー入力が残らないようにする。
終了したときは

array.length = 0;

間違えた時は

array.pop()

で完了

https://php-archive.net/javascript/js-typing/
https://murashun.jp/blog/20191110-18.html#chapter-4

後は問題を切り替える処理。

再代入するので

var messageNo = Math.floor( Math.random() * messages.length);
console.log(messages[messageNo]);

こちらに切り替えて"終わり"の後に記述したら、
Cannot read property 'charAt' of undefined
と怒られた。

messageNo = Math.floor( Math.random() * messages.length);
console.log(messages[messageNo]);

とすると次のお題がセットされたが打ち込めなかった。

console.log(messages[messageNo].charAt(num))

これで確認した所、空欄になっていた。
やはりちゃんと定義しないといけない。

何となくletに変えたら、さっきのエラーが出なかったが、console.log(messages[messageNo].charAt(num))では頭文字ではなく最後の模試が取得された。これはnumが最後のままだからだと思う。
だからnumを0にしてみる。
これをconsoleで返したら0になって次のお題も帰ってきて、その頭文字を打ち込んでもfalseと返ってきた。

本当に変わっているかを確認するため

window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
  console.log(messages[messageNo]);
//以下省略
};

で見たらお題が変わっていなかった。
これでは何を打ってもfalseになってしまう。
falseでも確認したらお題が切り替わってなかった。

たぶんif文の中で変えてもダメな気がする
グローバルスコープで変えないといけない。

関数にいれてみた。

function reset() {
  console.log("リセット")
  const messages = ['soccer', 'baseball', 'basketball'];
  var messageNo = Math.floor(Math.random() * messages.length);
  console.log(messages[messageNo])
  let num = 0
  console.log(num);
};

この関数を最後の文字のif文に書いたら
関数内では0になったが、また任意のキーを押したらnumもお題も戻ってしまう。

このリセット関数とlet=0に戻すのだけ通常の正誤判定の外に書いてみる。
それでもだめ

numをグローバルスコープに書くのがまずいのか?

頭が混乱してきたが要は関数内からグローバルスコープの変数を変更させたいのである。

reset関数を正誤判定のif文の一番最後に書いたらお題は変わるがnumだけ変わらなくなった。

どうしても厳しかったのでこちらの記事を参考にさせてもらいreset関数内のmessagesnumの定義のvarを外してグローバルで定義したら上手くいった。

##画面に表示
今まで作ったものを画面に表示させていく。
主にDOMを生成していく。
イメージとしては単語を表示させて、次に打つべき文字だけ異なる色で表示させたい。

まずは単語をまとめて表示する。
先頭に

const body= document.querySelector("body")
const doc = document.createElement("p")
doc.textContent=(messages[messageNo])
body.appendChild(doc)

で表示できる

色はこちらでつけようとしたら

Cannot set property 'Color' of undefined

色々調べた再度こちらのサイトを参考にさせてもらった。
textcontentではなくinnerHTMLで出力することでプレーンテキストではなくHTMLとして出力するので様々なことが出来る。

doc.innerHTML = (messages[messageNo].substr(0,num))+"<span style= 'color:red';>"+(messages[messageNo]).charAt(num)+"</span>"+(messages[messageNo].slice(num+1))

打つべき文字にはspanタグで色付けをして、それ以外はnumを使いsubstrsliceでその文字以降とその文字前後を組み合わせることで反映させるようにした。

これで完成である。

slice・substr・substringの違いはこのサイトで実際に触りながら確認できる
http://catprogram.hatenablog.com/entry/2013/05/13/231457

タイマーなどはまた今度

https://phpjavascriptroom.com/?t=js&p=stringobject
Diagram-Page-2 (2).png

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?