Firebaseでゲームにランキング機能を付ける

# 概要
個人的にFirebaseを勉強中。
ラフでも良いので成果物を作りたくて…

大昔に作った蛇のゲームにランキング機能をつけてみました。

# 結論
少し雑ですが下記のようになりました。
https://hashito.biz/snake/

<img width="1331" alt="スクリーンショット 2019-10-12 23.14.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/9c3d8086-ac84-39b6-e57a-6b14612097a0.png">

##仕組み
* ホームページ自体を`Firebase Hosting`に移動
* ユーザはログインをしなくてもランキング情報を参照できる(参照のみ)
* ログインしてゲームを行うと`得点情報`と`名前情報`を`Firestore`に保存  
 (このときの書き込み権限はユーザ毎にしか付与していません。)
* ランキング情報は`Firestore`の`並び替え`と`リミット`で作成


# 実装
ここではゲーム自体ではなくランキング機能部分を重点的に説明していきます。
前提として`Firebase`の初期設定や`Hosting`などの解説は飛ばしています。
ご了承ください…

## 1.データ定義
今回はシンプルに名前と得点だけです。
こちらは`Firestore`のUIにて操作して作成しました。
<img width="1276" alt="スクリーンショット 2019-10-12 23.25.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/54417aad-8d64-861e-36e6-ce6ecc778814.png">

基本的に、`Firestore`はno sql形式で保存されているらしく…
一番端のコレクションというのが`table`定義に近く、その中にドキュメントというkeyを持つフィールドが複数持てる仕組みなっています。
今回、私は`snake_users`というコレクションを定義し、ドキュメントにはユーザ固有IDを利用するようにしています。

こうしているのは次のルールの定義で**「自IDと同じドキュメント(key)には書き込みできる！」**と定義させるためです。

## 2.ルール定義

`Firestore`ではデータアクセスについてのルールを定義する事ができます。
これを利用すると**「全員が参照できる！」**とか**「この人しか編集できない！」**などが詳しく設定できます。

今回の定義はこんな感じです。
少し読めば分かると思うのですが…**前提として全員不許可**らしいです。
次に誰を許可するかと言うのを定義していく感じになります。

```
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
   match /snake_users/{userID} {
      allow read: if true;   
      allow write: if request.auth.uid == userID;
   }
  }
}
```

このルールでは注目するべきは真ん中の二行ですね。

```
      allow read: if true;   
      allow write: if request.auth.uid == userID;
```
一行目：**誰でもすべてのデータを見ても良い**
二行目：**ログインしているユーザのみデータを加工して良い**

ということです。
前準備はここまでで、コーディングになります。

## 3.コーディング
### 3.1.ログイン
今回、私は`google連携による認証`のみを利用しています。
ですので、ボタンクリックなどに下記のコードを実行させるだけで実現できます

```javascript
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function(result) {
}).catch(function(error) {
});
```

### 3.2.ログイン状態の確認
javascriptなどの実行時に認証済みのユーザかを確認する必要があると思います。
その場合は下記のようなコードで確認することが出来ます。

```javascript
firebase.auth().onAuthStateChanged(function(user) {
    if (user) {
        console.log("login")
    }else{
        console.log("no login")
    }
});
```

### 3.3.データの参照
今回、ランキング情報を参照するのに`Firestore`に書き込まれたデータを参照しています。
下記のような方法でこれを実現しています。

```javascript
var db = firebase.firestore();
var cnf = db.collection("snake_users")
cnf.orderBy('point').limit(10).get()
    .then(function(q) {
    self.ranking=[]
    var cnt = q.docs.length
    q.forEach(function(doc) {
        var data=doc.data();
        data.rank = cnt--
        self.ranking.unshift(data)
    });
})
.catch(function(error) {
    console.log("Error getting documents: ", error);
});
```

`orderBy('point')`で並び替えを行い、`limit(10)`で件数を制限してランキング情報を作成しているのが分かると思います。

## 3.4.データの登録
最後は登録です。
先程定義したルールでログインユーザーのみしか書き込みが出来ないため、「①ログインを確認」し、「②ログインユーザーIDでデータ書き込み」という手順でデータを書き込んでいます。

```javascript
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
    var cnf = db.collection("snake_users").doc(user.uid).set({
    name  : self.username,
    point : self.snake.point,
    }).then(function(docRef) {
    console.log("Document written with ID: ", docRef);
    self.get_ranking()
    }).catch(function(error) {
    console.error("Error adding document: ", error);
    });
}else{
    console.log("no login")
}
});
```

## 4.最後に少しだけ詰まった点
少しだけ詰まったのですが、Firebase Authを利用する場合に該当のドメインとサービス(今回はGoogle)を有効にする必要があります。
ご注意を…

<img width="1000" alt="スクリーンショット 2019-10-12 23.53.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/d41d484e-655d-fa78-df0a-0e87290ff9b0.png">
<img width="1021" alt="スクリーンショット 2019-10-12 23.53.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/c615b897-b072-4136-0f59-29e7a897ead6.png">

また、FirestoreなどはGCP(google cloud platform)と関連性が強いサービスなのでGCP側の設定が必要があるのでご注意を…

# 感想
Firebaseはかなり強力に感じました！
サーバーレスでここまで出来るんですから、色々と利用できそう…