Vue+Firebaseでタイピングゲームを作ってみた



##完成品
こちら
https://laughing-clarke-8dac9d.netlify.com/
Github
https://github.com/watatakahashi/linux-typing
イメージ
![Aug-16-2019 16-55-54.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305510/b4a095f3-c71a-76f7-9df7-bb74e00472f2.gif)



##経緯
- Linuxコマンドを何度学んでもすぐに忘れてしまう
- タイピング速度を上げるためにタイピング練習したい

会社の先輩や勉強会で出会った人を見てきて一番驚いたのは、経験者のタイピングが鬼速度なことでした。特にLinuxコマンドを打つ速さが尋常ではないのにびっくりして、「これが都会か（？）」「自分もカッコよくタイピングしたいな」という憧れを持つようになりました。

ちなみに私の実力は[寿司打](http://typing.sakura.ne.jp/sushida/)で10000万円でプラス2000円くらい。遅くはないと思うのですが、中学生時代から自己流でタイピングをし続けて「ホームポジション？なにそれおいしいの？」というレベルです。

普通にタイピングゲームで鍛えても良いのですが、Linuxコマンドも覚えながらの方が効率が良いんじゃないかということで、タイピングゲームを作って自分で問題を登録できるようにしました。

##設計

タイピングゲームなのでWebで作ります。VueとFirebase(Firestore)を組み合わせて作ることにしました。
Vueならタイピング画面の表現や、入力値のチェックが楽になるだろうと考えました。あと単純に使い慣れている言語のほうがよいというのもあります。ついでにTypescriptで型もしっかり書いていきます。
FirebaseからはFirestoreのみ使います。元々使っていて手っ取り早く使えるので良いと考えました。

##概要と詳細設計

- 問題を登録できる
    - Firestoreに登録する
-  登録されている問題からランダムで選び、シャッフルして出題
    - JSで書く
- ゲーム画面を作る
    - 入力したキーボードのキーコードを取得する
    - 入力した文字と問題文の文字と比較を、ひたすら繰り返す
    - 間違っていたら音を出す
    - タイピング画面は、VueでひたすらDOM操作
- ゲーム終了時にスコアを登録
    - Firestoreに登録
- スコアランキングを表示する
    - Firestoreから取得
- アップロード
    - Netlifyを使う

##ハマりどころ

###Firestoreの設計とデータの取得方法

当初はLinuxのコマンドだけの予定でしたが、ネットワークの部署の同期に見せたところ「Ciscoのコマンドでも同じの作ってよ」と言われたので、急遽Cisco用のゲーム画面と、それに伴いFirestoreに新しくテーブルを作ることになりました。せっかくなので、ある程度共通化できるところはすることにしました。

- １つのゲームごとに問題テーブル、ランキングテーブル、設定テーブルの３つを用意
- 複数のゲームがあっても、ゲームごとの設計は同じにしたい

結果的に、問題の取得は以下のようになりました。

```typescript:firestore.ts
export const getQuestionList = async (questionsTable: string): Promise<type.Question[]> => {
  let questionList: type.Question[] = []
  await main.db
    .collection(questionsTable)
    .where('valid', '==', true)
    .orderBy('createdAt', 'desc')
    .get()
    .then(querySnapshot => {
      querySnapshot.forEach(document => {
        const qs: type.Question = document.data() as type.Question
        questionList.push(qs)
      })
    })
    .catch(err => {
      console.log(err)
    })
  return questionList
}
```
ポイントは

- ゲームの種類によって、`questionsTable`の値だけを変える
- `Question`の型（問題ひとつの情報）はゲームの種類によらない

###問題登録時にバリデーションチェック

例えばLinuxコマンドには`cd src/hoge`のように「半角英数字記号」と「半角スペース」が入り混じった状態になります。そのため問題登録時に正規表現を使ってバリデーションチェックをしました。正規表現をあまり理解せず使っているので今後余裕があれば深く学んでみたいです。

```typescript:Home.vue
  @Watch('questionDraft.question')
  onWatchChanged(val: string): void {
    let reg = new RegExp(/^[a-zA-Z0-9!-/:-@¥[-`{-~\s]*$/)
    this.isDisabled = reg.test(val) && val != '' ? false : true
  }
```

###ミスタイプ時にキーに反応して音がうまく出ない
- [HTMLのaudioタグ](https://developer.mozilla.org/ja/docs/Web/HTML/Element/audio)では、ボタン連打時に効果音がうまく出ない
    - 例えば１秒の効果音だと、連打しても１秒間隔でしか流れない
- Web Audio API(AudioBuffer)を使うとうまく連打対応できた
    - [こちら](http://phiary.me/webaudio-api-getting-started/)を参考にさせていただきました

###環境変数をGitHubに載せない

しょうもない話なのですが。GitHub公開にあたってFirebaseのAPIキーなどの環境変数を直書きしたファイルをGithubにプッシュしていたので、念には念をということで変更履歴を消す作業をしました。

```typescript:main.ts

// Your web app's Firebase configuration
var firebaseConfig = {
  apiKey: process.env.VUE_APP_apiKey,
  authDomain: process.env.VUE_APP_authDomain,
  databaseURL: process.env.VUE_APP_databaseURL,
  projectId: process.env.VUE_APP_projectId,
  storageBucket: process.env.VUE_APP_storageBucket,
  messagingSenderId: process.env.VUE_APP_messagingSenderId,
  appId: process.env.VUE_APP_appId
}
// Initialize Firebase
firebase.initializeApp(firebaseConfig)
```

こんな感じに直して、あとは[こちら](http://baronyan.hatenadiary.com/entry/2014/07/14/152517)を参考にgitの変更履歴を削除しました。だたし[forceコマンドを使う時は要注意](https://next.rikunabi.com/journal/20170516_t12_iq/)らしいので、そもそも手戻りがないように進めることが大事なのですね。


##まとめと今後の課題
- いちから何かを作ることで勉強になった
    - 教科書どおりに行かず様々なことを調べないといけないのでめちゃくちゃ勉強になりました。
    - たとえば環境変数、OGP、Githubの設定方法など。
    - 基本的にググって調べるなのですが、エンジニアとしては必要な能力になってくる気がします。
- 見やすいコードにすることを心がけた
    - 学生時代から「動けばいいや」的な発想でゴリゴリ書くことが多く、コードがスパゲティ化することが日常茶飯事でした。恩師から「もっと綺麗にかければ。。。」と唯一指摘されたのは今でも覚えています。
    - Pythonのような動的型付け言語で書いていたときは「型付けしなくていいから楽だぜ〜」というノリでした（今思えばよく崩壊しなかったなと思います）。
    - 具体的にはTypescriptによる型付けや、Firestoreにアクセスする関数だけでもファイルを分けました。
    - 今後もこの考えを忘れないようにしたいです。
- 今後は機能面、デザイン面を洗練させたい
    - ジャンルを新規登録できるようにする
    - 管理用ページを作る
    - ランキングのリアルタイム反映する
    - デザインを整える
 
##参考

[Web Audio API で音を再生しよう
](http://phiary.me/webaudio-api-getting-started/)

[[JavaScript] 正規表現パターンサンプル集
](https://javascript.programmer-reference.com/js-regexp-sample/)

[gitでファイルの存在をなかったことにする](http://baronyan.hatenadiary.com/entry/2014/07/14/152517)