【jsで作ってみよう！】続々：javascriptを勉強して恐怖感を取り除こう！~タイピングゲーム作ってみる編~



##概略

効能：実際にそれっぽいことができるようになる。
対象：JavaScriptがわかってきたけど、実際に作るとなると一体何をすればっ！という方。
**注意：この記事は非常に長くなります。**


初めまして || こんばんわ。

僕の務めている会社で採用イベントが有りまして、僕もそれに参加しました。
そこで短時間でなんか作ろうというハッカソンイベントを学生に混ざってJsを使ってタイピングゲームのようなものを作りましたので、せっかくなのでこの場をお借りしてご紹介を。

5ヶ月前は僕も学生でした。懐かしい。

前回のレシピはこちらになります。
出来栄えは少々悪いですが、お口に合えば幸いです。
[javascriptを勉強して恐怖感を取り除こう！](http://qiita.com/mmusasabi/items/ec3740c50e3d47f5c56a)

[続：javascriptを勉強して恐怖感を取り除こう！~外部化編~](http://qiita.com/mmusasabi/items/8c6fc393057d6a19c036)


##まずは完成品はこちら。

こんなの作りました。

![TypingGame.png](https://qiita-image-store.s3.amazonaws.com/0/42310/204c18cd-d0a7-1d40-5322-f4e2d1a6506f.png "TypingGame.png")

シンプルですねヽ(´ｴ`)ﾉ
実際やってみましょう...

![TypingGame２.png](https://qiita-image-store.s3.amazonaws.com/0/42310/b3468630-b99e-05f8-2485-0ddf6348090a.png "TypingGame２.png")

一つに対しての制限時間短い！
しかもミスするとコメント流れてくる！？

![TypingGame３.png](https://qiita-image-store.s3.amazonaws.com/0/42310/75e26c39-20f7-fbb7-399b-7acc61d603ea.png "TypingGame３.png")

焦るしコメントうぜぇ！！

![TypingGame４.png](https://qiita-image-store.s3.amazonaws.com/0/42310/10bc4906-b091-8911-1623-3209125c223f.png "TypingGame４.png")

チーン＼(^o^)／

##【１２時間位で】まずはタイピング部分を作りましょうか【１からは無理です。】

タイトルにも有るように、ちょっと僕みたいなJs知識が浅い状態ですと１から色々作るのは短時間だと無理です。

なので、先人様のものをどんどん利用しましょう。

[パズルネットソフィア_タイピングゲームを作る](http://www.pori2.net/js/key/3.html)

僕は上記のサイト様を参考にソースを改造させ、目的のコードを作りました。

**この部分は先人様のソースを解読する部分になります。読めちゃった人は読み飛ばして次の項目へGO！**

とりあえず、理解する部分から始めましょう。
サイト様でも軽く説明されていますが、この記事ではこのソースを改造前提なのでこってり中身を追っていきます！！

サイト様のコードをそのまま利用すると、サイト様で紹介されている動作になると思うので画像は割愛です。
せっかくですので外部化している前提で話を進めますよ。大丈夫ですよね。

```js:typing.js
document.onkeydown = typeGame;  //キー押下時に関数typeGame()を呼び出す
//文字を格納する配列
var moji = new Array("Ａ","Ｂ","Ｃ","Ｄ","Ｅ","Ｆ","Ｇ","Ｈ","Ｉ",
                     "Ｊ","Ｋ","Ｌ","Ｍ","Ｎ","Ｏ","Ｐ","Ｑ","Ｒ",
                     "Ｓ","Ｔ","Ｕ","Ｖ","Ｗ","Ｘ","Ｙ","Ｚ");

//キーコードを格納する配列
var kcode = new Array(65,66,67,68,69,70,71,72,73,
                      74,75,76,77,78,79,80,81,82,
                      83,84,85,86,87,88,89,90);

//0～25までの乱数を格納する配列
var rnd = new Array();

//グローバル変数群
var mondai = "";       //問題の文字列を格納
var cnt=0;             //何問目か格納
var typStart,typEnd;   //開始時と終了時の時刻を格納
```

まずはこの部分から見て行きましょう。
出題する文字とキーボードが対応していないとお話になりません。
なので、mojiとkcodeという変数を用意して、キーボードとアルファベットを配列で作っています。

rndという変数は乱数を格納すると書いてありますね。０〜２５と言うのはアルファベットの個数ですね。
rndには問題となるキーの羅列が格納予定なんですね。

mondaiは表示部分。
cntは次に何を入力されたか測るポインター的な役目がありますね。

そして一番上のこの部分。
document.onkeydown = typeGame;  //キー押下時に関数typeGame()を呼び出す
これですが、タイピングゲームですのでキーボードの入力を感知しないといけません。
この記述のお陰でブラウザを開いてる状態でキーの入力を感知するのと同時、一番下の関数を動かしています。


```js:typing.js
//0～25までの乱数を200個作成して配列rndに格納する関数
function ransu()
{
  for ( var i = 0 ; i < 200 ; i++ )
  {
    rnd[i] = Math.floor( Math.random() * 26 );
  }
}
```

ここでは先ほど宣言したrndに合計２００個の配列をぶっこんでますね。イメージとしては
rnd = new Array(65,67,65,78,79,80,68,82,73............,89);
という感じですね。
つまり要求されるタイピング数は２００ですね。

```js:typing.js
//タイピングゲームの問題をセットする関数
function gameSet()
{
  //問題文とカウント数をクリアする
  mondai="";
  cnt=0;
  
  //乱数作成関数の呼び出し
  ransu();
  
  //問題文の作成（配列mojiの要素をランダムに200文字繋げる）
  //mondai= "" + moji[rnd[0]] + moji[rnd[1]] + … + moji[rnd[199]]となる
  for ( var i = 0 ; i < 200 ; i++)
  {
    mondai =  mondai + moji[ rnd[i] ];
  }
  
  //問題枠に表示する
  document.getElementById("waku").innerHTML = mondai;
}
```
これはゲームスタート時の関数ですね。
htmlのbodyタグの部分にonload="gameSet()"という記述があったと思います。
つまりbodyが読み込まれたらこの部分が走り始めて、jsが起動するわけです。
スタート画面などを作りたい方はこの関数より先にスタート画面が起動するようにjsを作れば問題無いですね。

さて、中身ですが、
まずは最初に宣言した変数をクリアしてますね。

なぜでしょう。
その理由は本ソース参考サイト様のSample動作を確認して頂ければわかると思います。
ボタンで問題初期化というものがありますね。
gameSet()関数ではどんどん変数に中身を追記していく仕組みを取っているので、スタートの段階で初期化してあげないと、問題文が更新されないんですね。

初期化した後はransu()が呼ばれて、問題となる文章（キーボードの数値の配列）がごちゃごちゃっとrndに代入されます。

その後、rndに代入された数値に合わせて、mondai変数にアルファベットをどんどん追記していって人間が見て分かる問題文が作られているわけですね。

その後、JSお馴染み
document.getElementById("waku").innerHTML = mondai;
こいつで、wakuというidのタグの中身に問題文を書き込んでいるわけです。

これでゲームスタートの状態が出来上がりましたね。


```js:typing.js

//キー入力を受け取る関数
function typeGame(evt)
{
  var kc;  //入力されたキーコードを格納する変数
  
  //入力されたキーのキーコードを取得
  if (document.all)
  {
    kc = event.keyCode;
  }
  else
  {
    kc = evt.which;
  }
  //入力されたキーコードと、問題文のキーコードを比較
  if (kc == kcode[ rnd[cnt] ])
  {
    //以下、キーコードが一致した時の処理

    //最初の1文字が入力された時間を記録する
    if (cnt==0)
    { 
      typStart = new Date();
    }
    
    cnt++; //カウント数を＋１にする
    
    //全文字入力したか確認
    if ( cnt < 200)
    {
      //問題文の頭の一文字を切り取る
      mondai = mondai.substring(1,mondai.Length);

      //問題枠に表示する
      document.getElementById("waku").innerHTML = mondai;
    }
    else
    {
      //全文字入力していたら、終了時間を記録する
      typEnd = new Date();
      
      //終了時間－開始時間で掛かったミリ秒を取得する
      var keika = typEnd - typStart;
      
      //1000で割って「切捨て」、秒数を取得
      var sec = Math.floor( keika/1000 );
      
      //1000で割った「余り(%で取得できる）」でミリ秒を取得
      var msec = keika % 1000;
      
      //問題終了を告げる文字列を作成
      var fin="GAME終了　時間："+sec+"秒"+msec;
      
      //問題枠にゲーム終了を表示
      document.getElementById("waku").innerHTML = fin;
    }
  }
}
```

最後に長いですが、キーが入力されてからの動きですね。

一番最初の
document.onkeydown = typeGame;  //キー押下時に関数typeGame()を呼び出す
これがキーの入力を感知して動作してもらえます。やった！

まず、kcという変数が宣言されて、その後event.keyCodeというのが代入されます。これは押されたキーのコードを取得しています。

- [キーコードに関してはこちらを参照下さい。](http://www.programming-magic.com/file/20080205232140/keycode_table.html)

その次のif文で一番最初の問題の文字コードと一致しているかをチェックしています。
合致していれば
＞もし入力位置文字目なら！ => 入力開始時間を記録！
＞cntを増やす。次の文字の文字コードを参照するようにする。
＞全文字入力し終えたかチェック。 => まだなら先頭の文字を切り取って、問題文を更新。
＞全文字入力し終えたなら => 入力終了時刻を記録。差分を割り出し、入力終了までかかった時間を問題文を出していたところに出力！

こんな感じでしょうね。
この文章難なく読み進めれた人は、きっとかなりJS慣れしていると思います。

###補足：このへんが気になった方います？

```
  //入力されたキーのキーコードを取得
  if (document.all)
  {
    kc = event.keyCode;
  }
  else
  {
    kc = evt.which;
  }
```
これ何してんのって話ですが、平たく言えばブラウザ互換ですね。
event.keyCodeはつまりブラウザで起こったイベント（今回で言うとキー入力）が取得できるはずなのですが、不思議なことにFireFoxだと動きません。ナンテコッタイ。
eventがFireFoxだとグローバルオブジェクトではないことが原因だそうですが、よくわからん。

ので、この関数はキーボートが押された際に呼ばれるのを利用して引数にevtという変数が有りますよね。これは取得方法は違えどほぼeventオブジェクと一緒なんですけど、これを.whichを使ってキーコードを取得しています。

ブラウザ対応はなかなか骨に来ますね。


##じゃあ改造しましょうか。

まずはどんな改造を施せばいいのか。完成品の部分と現在の部分を比較しましょう。
持てる知識を総動員して...

- 問題を複数作って、そのなかからランダムで出題したい。
- 入力したらどこまで打ったか表示されるようにしたい。
- １問に対して制限時間付けたい。
- デザインをガラッ★と変える。
- コメント流したい（！？）
- 総タイプミス数をカウントする

こんなところですかね？
では順番に行きましょう。
毎回記述後コードコピペしてたらやたら同じコードが出現することになると思うので、パーツパーツ必要事項を投下していって、最後にソースを載せますね。

###タイピング問題を制作して、入力文字済み表示をと分ける。。

この部分さえ解決できればほかなんて微々たる改造よ！

まずはデータを作りましょう。
この作ったものはDBを利用して作ったんだけど

(一応流れはこんな感じ。)

```php:conectDB.php
<?php
//PDOで接続準備
//接続
//sql用意
$statement = $pdo->query($sql);//データを取得
$row = $statement->fetchAll(PDO::FETCH_ASSOC);//データを配列化
//接続閉じる
?>

//htmlのheadの中
//phpで作った配列をjson化してJSで受け取る。受けったjs側はそれを配列へとパースして、変数datとして受け取る。
<script> var dat = JSON.parse('<?php echo json_encode($row);?>'); </script>
//注意：変数用意するより前に外部jsを読み込むと変数が用意されてないのを参照しようとしてバグるので注意
<script type="text/javascript" src="js/typing.js"></script>
```
（jsでDB接続する方法もあったんだけど、時間がかかりそうだったので今回は省略。兎に角今回は時間がない。ある知識で間に合わせる優先。）

でもDBの話をすると長くなるのでもう既に配列で用意してあることにしましょう。

ということでこんな感じのデータを使います。

```js
//description＞単語の説明文　word＞問題単語。
var mondaiDate = [
{ "description": "ファイルのアクセス権を調べる", "word": "access"},
{ "description": "ファイルのオープン", "word": "fopen"},
{ "description": "標準入力からの1文字入力", "word": "getchar"},
{ "description": "標準出力への書式付き出力", "word": "printf"},
{ "description": "標準入力からの書式付き入力", "word": "scanf"},
];
```

次に、問題文が文字有りきですね。ということは処理としてransu()で

- 今何問目かチェックする。
- ◯問目の文字をチェックする。
- 対応するキーコードをrndに覚えさせる。

ということをして入力するキーコード部分を整理せねばなりません。更に小文字ですので、moji配列の小文字版も必要です。となると

```js
//小文字追加。
var moji_s = ["a","b","c","d","e","f","g","h","i",
            "j","k","l","m","n","o","p","q","r",
            "s","t","u","v","w","x","y","z"];
var ans = 0;            //回答数
function ransu(mondai)
{
  for ( var i = 0 ; i < mondai.length ; i++ )
  {
    //i番目の文字列を探す
    var serch = mondai.charAt(i);
    //i番目にある文字列の番号を探す。
    rnd[i] = moji.indexOf(serch);
    if( rnd[i] < 0 )
    {
      rnd[i] = moji_s.indexOf(serch);
    }
  }
}
```
こんな風に書き換えれれば良さそうですね。

gameSet()関数には
mondai = dat[ans]['word'];
description = dat[ans]['description'];
のように記述して、呼ばれるたびに次の問題を初期値にしてあげて、
typeGame()関数には
全文字入力された時にans++し、gameSet()関数を呼ぶ。
全問題回答した(ans == dat.length-1)ら終了文表示。の感じで作ればこの部分OKですね。

###１問に対して制限時間を付けたい！
こちらはやることとしては案外簡単です。
まずは **timeCount()とgameFinish()** という関数を用意しましょう。
やらせることは簡単でこちらが用意した制限時間に因んで残り秒数が表示されるようにして、０になったら
gameFinish()いろいろな処理を終わらせて、終了の表示に持って行きましょう。

```
timer = setInterval("timeCount()",100);
```
のように記述することで、0.1秒毎にtimeCount()関数を動かすことが出来ます。
これで0.1秒刻みでカウントして、0になる瞬間を監視できますね。
次の問題に移行する度に
そして0になったらclearTimeout(timer)という記述で繰り返し動作を止めて、終了画面を出せばOKです。

###デザインを色々変更したいぞぉ！

この辺をjsの力で色々やるのは大変です。
ここはドンとhtmlさんの力を借りましょう。

```html:typing.html
<div id="nicoscreen">
  <p id='time'>最初の一文字入力でスタート</p>
  <div class='question'>
    :__<font id='mondai'></font>__;
  </div>
  <p id='input'></p>
  <p id='description'></p>
  <p id='missType'></p>
  <button id="retry" type="button"
onclick="location.reload()" style="visibility:hidden">
    もう一度行う
  </button>
</div>
```

もっと綺麗にできそうですが、ひとまずはこんな感じで！
この部分をidに則ってcssを色々改造することで文字フォントとか位置、背景画像の設定ができますね。

###コメントを流す＆ミスタイピングをカウントする！

上記のソースに[nicoscreen]ってidを設定しているdivタグが有りますよね。
勿論こんなの１から作っていたら間に合いません。あるものを使っちゃいましょう。

[NicoScreen.js](http://labs.r9game.com/nicoscreen/)

これは良いものですね！
このサイトにある３番のような感じでの使用法を使いましょう。

このjsはjQueryを使用していますので

```html
  <script type="text/javascript" src="js/jquery.js"></script>
  <script type="text/javascript" src="js/nicoscreen.js"></script>
```

こんな感じで並べて引用しましょう。ちなみにjqueryですけど

```
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>

```

と記述することで手元にDLしなくても使用できますよ。ただ、ネット環境がないと検証できないので、ローカルでがちゃがちゃやるなら手元に持っておくのもいいと思います。

すると、nicoscreenとidのある空間に

nicoscreen.add('こめんと');

とすることでコメントを流すことが出来ます。簡単。

```js
//misstyping時のコメント
dat2  = [
{ "message": "m9(^Д^)ﾌﾟｷﾞｬｰ"},
{ "message": "wwwwwwwwwwwwwwwwww"},
{ "message": "＼(^o^)／たいぷみす？／(^o^)＼ねぇねぇタイプミス？"},
{ "message": "ちょwwwおまwwwwwwミスとかwwwwwwwww"},
{ "message": "ヽ(´ｴ`)ﾉがんばれ"},
];
//完全クリア時のコメント
dat3  = [
{ "message": "ヽ(*´∀｀)ﾉｵﾒﾃﾞﾄ─ｯ♪"},
]
```

こんな感じでコメントのデータを用意して、ランダムで当て込んで

```
//ランダムでミス時のメッセージを入手する。
miss_string = dat2[Math.floor(Math.random() * dat2.length)]['message'];
nicoscreen.add(miss_string);
```

ミスタイピング時の動作に追加すればいいですね。

ミスタイピングのカウントは、それこそミスタイピングの時に
適当に変数用意して、インクリメントしていけば問題無いですね。

また、問題が全部解決している時の追加でのタイプ入力を防いで余計なコメントが流れないようにすることを考えると

```js
//入力されたキーコードと、問題文のキーコードを比較
if (kc == kcode[ rnd[cnt] ] && ans != dat.length){
 //タイプ成功時の動作
}else if(ans != dat.length){
 //タイプミス時の動作
}
```

こんな処理でいいかな？
ここでansは正答数を記録する変数です。


##今回僕が作ったソースはこんな感じです。

多分10時間程度で作った時のソースをそのまま載せます。

ディレクトリの構造はこんな感じ。
typingGame
┗typing.html
┗css
　┗common.css
┗js
　┗typing.js
　┗nicoscreen.js
　┗jquery.js
┗img
　┗backGround.png

###html部分

```html:typing.html
<!DOCTYPE html>
<html lang="ja">
<head>

  <link rel="stylesheet" type="text/css" href="css/typing.css">

  <script type="text/javascript" src="js/typing1.js"></script>
  <script type="text/javascript" src="js/jquery.js"></script>
  <script type="text/javascript" src="js/nicoscreen.js"></script>

  <!-- ページの文字コード決定 -->
  <meta charset="utf-8">
  <!-- 自分で作ったjavascriptの宣言 -->
  <title>typing</title>
</head>
<body onload="gameSet()">
  <div id="main_typing">
    <!-- コメントを流す範囲を指定（CSSにて） -->
    <div id="nicoscreen">
      <!-- 時間の表示 -->
      <p id='time'>最初の一文字入力でスタート</p>

      <div class='question'>
        <!-- 出題 -->
        :__<font id='mondai'></font>__;
      </div>
      <!-- タイピング済みの部分の表示 -->
      <p id='input'></p>
      <!-- 出題の説明 -->
      <p id='description'></p>
      <!-- ミスタイプ数の表示 -->
      <p id='missType'></p>

      <button id="retry" type="button"
    onclick="location.reload()" style="visibility:hidden">
        もう一度行う
      </button>

    </div>
  </div>

  </div>
  <!-- コメントを流すステータスの設定 -->
  <script type="text/javascript">
    var obj = {
      "base":{
        color:"white",
        speed:"fast",
        interval:"fast",
        font_size:"24px",
        loop:true
      },
      "comments":[
        ]  
      };

      nicoscreen.set(obj);
      nicoscreen.start();
      function addComment(){
        var str = $("#text_comment").val();
        nicoscreen.add(str);
        $("#text_comment").val(""); 
      }
  </script>
  </body>
</html>
```

###js部分

```js:typing.js

document.onkeydown = typeGame;  //キー押下時に関数typeGame()を呼び出す

//文字を格納する配列
var moji = ["A","B","C","D","E","F","G","H","I",
            "J","K","L","M","N","O","P","Q","R",
            "S","T","U","V","W","X","Y","Z"];
var moji_s = ["a","b","c","d","e","f","g","h","i",
            "j","k","l","m","n","o","p","q","r",
            "s","t","u","v","w","x","y","z"];
//キーコードを格納する配列
var kcode = new Array(65,66,67,68,69,70,71,72,73,
                      74,75,76,77,78,79,80,81,82,
                      83,84,85,86,87,88,89,90);

//0～25までの乱数を格納する配列
var rnd = new Array();
//制限時間。０を含めた秒数。２なら、３秒
var limit = 3;
var sec,msec,timer;
var miss_string,success; 
//スコア変数
var rest= 0;
var score = 0;
//グローバル変数群
var mondai      = "";   //問題の文字列を格納
var input       = "";   //打ち込み内容
var description = "";   //説明文の内容
var mondaiCount = "";   //問題の文字列を格納
var missType    = 0;    //入力ミスの回数
var cnt = 0;            //入力数

var ans = 0;            //回答数

var endFlag = false;    //時間切れになったかどうか
var typStart,typEnd;    //開始時と終了時の時刻を格納

dat  = [
{ "c_description": "ファイルのアクセス権を調べる", "c_word": "access"},
{ "c_description": "ファイルのオープン", "c_word": "fopen"},
{ "c_description": "標準入力からの1文字入力", "c_word": "getchar"},
{ "c_description": "標準出力への書式付き出力", "c_word": "printf"},
{ "c_description": "標準入力からの書式付き入力", "c_word": "scanf"},
];
//misstyping時のコメント
dat2  = [
{ "message": "m9(^Д^)ﾌﾟｷﾞｬｰ"},
{ "message": "wwwwwwwwwwwwwwwwww"},
{ "message": "＼(^o^)／たいぷみす？／(^o^)＼ねぇねぇタイプミス？"},
{ "message": "ちょwwwおまwwwwwwミスとかwwwwwwwww"},
{ "message": "ヽ(´ｴ`)ﾉがんばれ"},
];
//完全クリア時のコメント
dat3  = [
{ "message": "ヽ(*´∀｀)ﾉｵﾒﾃﾞﾄ─ｯ♪"},
]

//問題の文字列をキーボード番号に対応させる。
function ransu(mondai)
{
  for ( var i = 0 ; i < mondai.length ; i++ )
  {
    //i番目の文字列を探す
    var serch = mondai.charAt(i);
    //i番目にある文字列の番号を探す。
    rnd[i] = moji.indexOf(serch);
    if( rnd[i] < 0 )
    {
      rnd[i] = moji_s.indexOf(serch);
    }
  }
}

//タイピングゲームの問題をセットする関数
function gameSet()
{
  cnt=0;
  
  mondai = dat[ans]['c_word'];
  description = dat[ans]['c_description'];

  mondaiCount = mondai.length;
  //乱数作成関数の呼び出し
  ransu(mondai);

  //問題枠に表示する
  document.getElementById("mondai").innerHTML =  mondai;
  document.getElementById("input").innerHTML =  "<font class='test'>＞</font>";
  document.getElementById("description").innerHTML = description;
}

function timeCount(){
  //全文字入力していたら、終了時間を記録する
  typEnd = new Date();
  
  //終了時間－開始時間で掛かったミリ秒を取得する
  var keika = typEnd - typStart;
  //1000で割って「切捨て」、秒数を取得
  sec = limit - Math.floor( keika/1000 );
  
  //1000で割った「余り(%で取得できる）」でミリ秒を取得
  msec = 9 - Math.floor( (keika / 100) % 10);
  
  //問題終了を告げる文字列を作成
  var countTime="GAME終了まであと："+sec+"秒"+msec;

  //現在残り時間の代入
  document.getElementById("time").innerHTML =  countTime;

  //スコア計算(余った秒数を足して行く)
  rest = 4000 - keika;
  score += rest;

  //時間切れになった際の処理
  if(sec == 0 && msec == 0)
  {
    document.getElementById("time").innerHTML =  '時間切れです。';
    document.getElementById("mondai").innerHTML = '正答数：' + ans + '/' + (dat.length) + 'スコア：' + score;
    clearTimeout(timer);
    endFlag = true;
    gameFinish();
  }
}


//キー入力を受け取る関数
//最初の一文字目が入力された時点でスタート
function typeGame(evt)
{
  if(endFlag == false){
    var kc;  //入力されたキーコードを格納する変数
    //入力されたキーのキーコードを取得
    if (document.all)
    {
      kc = event.keyCode;
    }
    else
    {
      kc = evt.which;
    }

    //入力されたキーコードと、問題文のキーコードを比較
    if (kc == kcode[ rnd[cnt] ] && ans != dat.length)
    {
      //以下、キーコードが一致した時の処理
      //最初の1文字が入力された時間を記録する
      if (cnt==0 && ans==0)
      {
        timer = setInterval("timeCount()",100);
        typStart = new Date();
      }
      
      cnt++; //カウント数を＋１にする
      
      //全文字入力したか確認
      if ( cnt < mondaiCount)
      {
        //問題文の頭の一文字を切り取る
        input  = mondai.substring(0,cnt);

        //問題枠に表示する
        document.getElementById("mondai").innerHTML =  mondai;
        document.getElementById("input").innerHTML =  "<font class='test'>＞</font>" +  input;
        document.getElementById("description").innerHTML = description;
        document.getElementById("missType").innerHTML = '合計ミスタイプ：' + missType + '回';
      }
      else
      {
        if(ans == dat.length-1)
        {
          ans++;
          //全文字入力していたら、終了時間を記録する
          typEnd = new Date();
          //終了時間－開始時間で掛かったミリ秒を取得する
          var keika = typEnd - typStart;
          //1000で割って「切捨て」、秒数を取得
          sec = Math.floor( keika/1000 );
          //1000で割った「余り(%で取得できる）」でミリ秒を取得
          msec = keika % 1000;
          //問題終了を告げる文字列を作成
          var fin="GAME終了　時間："+sec+"秒"+msec;
          //問題枠にゲーム終了を表示

          //タイマー終了
          for(var i=0 ; i < 3 ; i++){
            success = dat3[Math.floor(Math.random() * dat3.length)]['message'];
            nicoscreen.add(success);
          }
          clearTimeout(timer);
          document.getElementById("mondai").innerHTML = '全問正答！スコアは'+ score;
          gameFinish();
        }
        else
        {
          ans++;
          //一題解いたら時間リセット
          typStart = new Date();
          gameSet();
        }
      }
    }
    else if(ans != dat.length-1)
    {
      missType++;
      //ランダムでミス時のメッセージを入手する。
      miss_string = dat2[Math.floor(Math.random() * dat2.length)]['message'];
      nicoscreen.add(miss_string);
      
      document.getElementById("missType").innerHTML = '合計ミスタイプ：' + missType + '回';
    }
  }
}

//キー入力を受け取る関数
function gameFinish(){
  document.getElementById("time").innerHTML =  '終了';
  document.getElementById("description").innerHTML = 'お疲れ様でした。';
  document.getElementById("input").innerHTML =  "<font class='endTitle'>program</font> -> end;";
  document.getElementById("missType").innerHTML = '合計ミスタイプ：' + missType + '回';

  document.getElementById("retry").style.visibility = 'visible';

}

```

###cssの部分

```css:typing.css
/*BODY {
  background-image: url("../images/backGround.png"); /* 全体の背景画像 
  background-repeat: no-repeat;            /* 背景を繰り返さない 
  background-position: 50% 50%;            /* 背景画像の位置は画面中央 
  background-attachment:fixed;             /* 背景画像を固定する 
}
*/

body, html {
  color: #ffffff;
  background-color: #ffffff;
}

#side{
	width:30%;
	float:left;
}

#main_typing{
	width: 70%;
  background-image: url("../images/backGround.png"); /* 全体の背景画像 */
  background-repeat: no-repeat;            /* 背景を繰り返さない */
  background-position: 50% 50%;            /* 背景画像の位置は画面中央 */
  background-attachment:fixed;             /* 背景画像を固定する */
}

#main_typing .share{
  background-color: #666;
  color: #fff;
  width: 100%;
  height: 100px;
}

#nicoscreen{
  background-color:rgba( 0, 0, 0, 0.7);
  color: #fff;
  width: 100%;
  height: 450px;
  font-size: 18pt;
  overflow: auto;
  text-align: center;
}
#nicoscreen .test{
  color: #7fff00;
}
#nicoscreen a{
  color: #fff;
}

#missType{
  color: #ddd;
  font-size: 13pt;
}

.question{
  background-color:rgba( 100, 100, 100, 0.7);
  color: #fff;
  width: 100%;
  height: 15%;
  font-size: 18pt;
  overflow: auto;
  text-align: center;
  padding-top:10px;
  margin:25px 0px 15px 0px;
}
```


##反省点

- 問題開始前から入力受付できちゃう。タイプミスでコメント流れてくる。
- 問題のやり直しがページヘのリダイレクト。スマートじゃないなぁ。
- 背景が動かないでそのまんま。なにかアクション欲しかったなぁ。

それ以外にももっと色々出来そうだけど、きりがいいんでこのへんで！

けっこう色々遊ぶにはちょうどいいソースだと思うので、いじくり回してみてください！

こんなところで今回は終わりです！

俺（達）の勉強はこれからだ！


次何やろうかなぁ。そろそろjqueryとかAjaxとかに手を出してみようかな。ネタ募集してます（笑）