Help us understand the problem. What is going on with this article?

ゲームにおけるFirebase 活用例

PONOS Advent Calendar 2019の24日目の記事です。
🎄メリークリスマス!!クリスマス・イブにFirebaseの記事をお届けします!!!🎄
🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁🎁

はじめに:eyeglasses:

現在運用中のゲームでFirebaseを導入しました。
この記事では活用事例とノウハウを紹介していきます。
Firebase利用の一例になれば幸いです。

組み込み方法は別記事をご参照ください
公式ドキュメント
iOS
Android
Unity
C++
Web

この記事の対象者:eyeglasses:

・Firebase初心者、中級者
・Firebaseを検討中の方
・Firebaseの実装イメージを掴みたい方
・サーバーレスを検討中の方
・Googleのサービスが好きな方、興味のある方

Firebaseの各機能をざっくり紹介

機能がたくさんあります

名称 機能 活用
Firebase Authentication 認証機能
Firebase Realtime Database NoSQL クラウド データベースでデータの保管と同期を行うことができる
Cloud Firestore Realtime Database からさらにパワーアップした NoSQL データベース。
Cloud Storage for Firebase 写真や動画などのコンテンツを保管、取得することができる。
Firebase Hosting webサイトを構築
Cloud Functions for Firebase 関数ごとにサーバー処理を簡単に用意できる
ML Kit for Firebase 機械学習 ×
Firebase Crashlytics クラッシュ レポート管理
Firebase Performance Monitoring パフォーマンスの問題を診断
Firebase Test Lab 様々なデバイスでアプリの自動テストおよびカスタマイズされたテストを実行
Firebase Cloud Messaging サーバから通知機能
Firebase In-App Messaging ターゲットを絞り込んでアプリ内で通知 ×
Firebase Predictions 機械学習を用いた分析ツール
Firebase Remote Config Firebase consoleから、外観の変更、機能の段階的な展開などをカスタマイズ ×
Google Analytics for Firebase 総合的な分析ツール
Firebase A/B Testing ユーザーごとに出し分けしてA/Bテストを行う
Firebase Dynamic Links 複数のプラットフォームで機能するURLを生成 ×
Firebase App Indexing アプリを Google 検索結果に表示することができる ×

活用1:ログイン時のユーザー認証

Firebase Authenticationをゲーム内での認証機能として使っています。
ログイン時に認証して、Firebase各種機能と連携して認証済みのアカウントしかセキュリティー的に許可しないようにしています。
以下の4種類の認証を使っています。
・Google
・Facebook
・メールアドレス
・匿名認証

匿名認証で作成したデータと各種連携したデータを紐づける処理はこちらで実装する必要がありますが、
公式ドキュメントにもサンプルコードが用意されているので低コストで実装できます。

匿名認証のコード

Auth.cpp
#include "firebase/app.h"
#include "firebase/auth.h"

void Auth::signInAnonymously(){
    firebase::App* app = App::GetInstance();
    firebase::auth::Auth* auth = Auth::GetAuth(app);

    auth->SignInAnonymously().OnCompletion([this](const firebase::Future<firebase::auth::User*>& result){
        if (result.status() == firebase::kFutureStatusComplete &&
         result.error() == firebase::auth::kAuthErrorNone &&
         result.result()) {
            //成功処理
        }else{
           //失敗処理
        }
    });
}


活用2:通信時の不正対策

通信時の不正対策として
Firebase Authentication

Cloud Functions
を組み合わせてサーバーサイドでも認証チェックしております。

Cloud Functionsを使用するとクライアント側の通信処理もセキュアでシンプルな作りになります。
詳細はCloud Functionsのドキュメントを一読するのもありです。

クライアント側の通信処理
Cloud Functionsで定義したsampleにリクエストを投げます。

Request.cpp
void Request::callSample(){
    firebase::Variant data = firebase::Variant::EmptyMap();

    firebase::functions::HttpsCallableReference doSomething = functions->GetHttpsCallable("sample");
    doSomething.Call(data).OnCompletion([this]
    (firebase::Future<firebase::functions::HttpsCallableResult> result){
        if(result.status() == firebase::kFutureStatusComplete &&
        result.error() == firebase::functions::kErrorNone && result.result()){
        //成功処理
        }else{
        //失敗処理
        }
    });
}


サーバー側の認証チェック
exports.sampleの部分がレスポンスを返します。

Server.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
 
exports.sample = functions.https.onCall((data, context) => {
    if(!checkAuth (data, context) ){
        throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +'while authenticated.');
    }
    //以降でサーバー処理を記入していく 
    return {
            text:"success",
    };
});


//認証チェック
async function checkAuth (data, context) {
    if( !(context.auth && context.auth.uid && context.auth.token)){
        console.log('checkAuth error');
        return false;
    }
    const user = await admin.auth().getUser(context.auth.uid);
    if (user.customClaims && user.customClaims.admin !== true) {
        console.log('checkAuth error');
        return false;
    }
    return true;
}

活用3:サーバーからのPush通知

Cloud Messagingを活用して特定条件を満たしたユーザーにPush通知を送るようにしています。
このサンプルソースではRealTimeDBにユーザーのpushTokenが保存されており、pushTokenを取り出して
使用しているパターンです。

Server.js
exports.pushNotification = functions.https.onCall((data, context) => {
    if(!checkAuth (data, context) ){
        throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
        'while authenticated.');
    }


    const userId = data.userId;
    var pass = "user_table/" + userId + "/" + "pushToken";
    return admin.database().ref(pass).once('value').then(function(snapshot){
        var pushToken = snapshot.val();
        const payload = {
            notification: {
              title: 'プッシュ通知タイトル',
              body: `プッシュ通知本文`
            }
          };
          return admin.messaging().sendToDevice(pushToken, payload);
    }).catch(error => {
          console.error(error);
          throw new functions.https.HttpsError(error);
    });
});

シンプルに書くとこれだけで通知を送れます。

          return admin.messaging().sendToDevice(pushToken, payload);

活用4:バトルログの保存(アップロード)

バトル時のログや詳細情報はCloud Storageに保存しています。
DBに保存すると量と値段の面で、デメリットがあったので分析の必要がなく、他のユーザーから参照されないデータはCloud StorageにGZip化して保存しています。

CloudStorage.cpp
#include "firebase/app.h"
#include "firebase/storage.h"

void CloudStorage::storageUpLoad(){
    firebase::App* app = App::GetInstance();
    firebase::storage::Storage* storage = Storage::GetInstance(app);

    //アップロード先のパス
    std::string serverPath = "/";
    //アップロードするファイル保存先
    std::string localPath = "file://" + "ローカルパス";

    storage->GetReference().Child(serverPath).PutFile(localPath.c_str()).OnCompletion([](const firebase::FutureBase& result) {
                if (result.error() == firebase::storage::kErrorNone){
                  //成功処理
                }
                else {
                  //失敗処理
                }
    });

}


活用5:リソースダウンロード

これもCloud Storageを活用しています。
リソース数、容量などによって状況は変わると思いますが、問題ない速度でダウンロードできています。
何よりもCloud Storageはコストパフォーマンスがいいです。
現在、ほぼ無料枠で済んでいます。

CloudStorage.cpp
#include "firebase/app.h"
#include "firebase/storage.h"

void CloudStorage::storageDownload(){
    firebase::App* app = App::GetInstance();
    firebase::storage::Storage* storage = Storage::GetInstance(app);

    //対象リソースのパス
    std::string serverPath = "/";
    //ダウンロード後の保存先
    std::string localPath = "file://" + "ローカルパス";
    storage->GetReference().Child(serverPath).GetFile(localPath.c_str()).OnCompletion([](const firebase::FutureBase& result) {
        if (result.error() == firebase::storage::kErrorNone) {
         //成功処理
        }
        else {
         //失敗処理
        }
    });
}


活用6:ゲーム内お知らせ

お知らせ機能はFirebase Hostingを活用しています。
環境構築などの導入は非常に簡単です。
5分くらいでできると思います。
環境構築

環境構築が終わったら、HTML,CSS,画像などのWEB素材を用意して

firebase deploy

で、デプロイして完了です。

固定のお知らせやヘルプページを見せるだけでしたらFirebase Hostingだけで十分なのですが
ユーザーごとに見せるページを変えたい、期間で出し分けをしたい
などの仕様によってはサーバー側でHTMLを生成する必要があるので
うちのゲームではCloud Function、RealTimeDatabaseなども絡めて使っている箇所があります。

注意点としては
過去にデプロイしたWEBページがCDNに溜まっていき、容量が無駄になるので
Firebaseコンソール上からときどき消去しないといけません。
WEBページに問題があったときに、とっさにロールバックできるのでそれとトレードオフという感じですね

活用7:KPI 分析

ゲーム運営にKPI 分析は必須ですよね
Google Analytics for Firebaseを使っています。
for Firebaseと書いていますが、バックエンドの仕組みはGoogle AnalyticsでKPI分析するうえでは違いはありません。違いは組み込み方法が若干違うくらいです。

スクリーンショット 2019-12-19 18.55.43.png

こちらのFirebase デモプロジェクトを触ってみるのが理解が早いです。

1.デモプロジェクトログイン
デモプロジェクトが見れるようになったらAnalyticsページへ
2.デモプロジェクト Analyticsページ
※リンクから飛べない場合は、左側のメニューにてアナリティクス配下のDashboardをクリック

Analyticsは組み込むだけでセッション数、DAU、課金額、ARPPUなど
デフォルトでいくつかの値を集計してくれます。
ゲーム内独自の集計をしたい場合は、イベントを実装すればできます。

イベント実装例

FirebaseAnalytics.cpp
#include "firebase/app.h"
#include "firebase/analytics.h"

void FirebaseAnalytics::init(){
    firebase::App* app = App::GetInstance();
    firebase::analytics::Initialize(*app);
}

//レベルアップ時、イベント登録
void FirebaseAnalytics::levelUp(){
    firebase::analytics::LogEvent(firebase::analytics::kEventLevelUp);
}

活用8:予測データの運営活用

Firebase PredictionsではGoogleの機械学習の予測結果を簡単に知ることができます。

スクリーンショット 2019-12-19 19.03.49.png

デモプロジェクト

以下項目の一週間後の予測結果を知ることができます。
churn離脱ユーザーの数

not_churn継続ユーザーの数

spend課金するユーザーの数

not_spend課金しないユーザーの数

Firebase Predictionsの優れている点は
これらの予測結果に対して
・A/Bテストを行なって動向を分析できる
・Remote ConfigによってUIやパラメーターなどを瞬時に変えることができる
・Cloud Messagingを使ってお得な情報などを送ることができる
ことにあると思います。

また、Google Analyticsと連携してコンバージョンに設定されたイベントを予測項目として追加することもできます。
ゲームごとに有効なイベントを設定すれば有効活用できると思います。

活用9:バナー広告、動画広告

Google AdMobを使って、バナー広告、動画広告に対応することができます。
Firebaseの機能としてあまり注目されてないですが、実はFirebase SDKではAdMobの機能もサポートされてます。

バナー広告表示

FirebaseAdmob.cpp
#include "firebase/app.h"
#include "firebase/admob.h"
#include "firebase/admob/banner_view.h"


void FirebaseAdmob::createBanner(){
    //firebase::admob::BannerView* banner_view
    banner_view = new firebase::admob::BannerView();

    firebase::admob::AdSize ad_size;
    ad_size.ad_size_type = firebase::admob::kAdSizeStandard;
    ad_size.width = 320;
    ad_size.height = 50;
    // my_ad_parent is a reference to an iOS UIView or an Android Activity.
    // This is the parent UIView or Activity of the banner view.
    // kBannerAdUnitはAdMobのサイトで発行されるId
    banner_view->Initialize(getAdParent(), kBannerAdUnit, ad_size);
}

void FirebaseAdmob::showBanner(){
    banner_view->MoveTo(banner_view->kPositionBottomLeft);
    banner_view->Show();
    firebase::admob::AdRequest my_ad_request = {};
    banner_view->LoadAd(my_ad_request);
}

活用10:多端末テスト

スマホゲーム開発において多端末検証って大変ですよね
特にAndroidとかAndroidとかAndroidとか・・・
そこで活躍するのが
Firebase Test Lab
Google データセンターでホストされているデバイス上でアプリをテストします。
1 回のオペレーションで、さまざまなデバイス、
さまざまなデバイス構成で Android アプリや iOS アプリをテストし、
Firebase consoleで結果(ログ、動画、スクリーンショットなど)
を確認できます。

・Android端末が足りない場合
・テスターが足りない場合
・QA開始前のシステム的なテストをしたい場合
など使いどころはたくさんあります。

スクリーンショット 2019-12-19 19.04.49.png

1.デモプロジェクトログイン
デモプロジェクトが見れるようになったらTest Labページへ
2.デモプロジェクト Test Labページ
※リンクから飛べない場合は、左側のメニューにて品質、配下のTest Labをクリック

おわりに

Firebaseを導入してみての感想としては・・・

非常に簡単、便利で素晴らしいです!
ゲーム仕様や料金面での検討をする必要はありますが
まずは無料プランで使ってみてから他のサービスと比較検討する方法がオススメです!

料金面にて実装で創意工夫した箇所もありますが、結果として今は超・低コストで運営できています。

以下の機能は無料で使い続けることができるので、組み込んでおいて損はないです
・Google Analytics for Firebase
・Firebase Authentication
・Firebase Crashlytics
・Firebase Performance Monitoring
・Firebase Cloud Messaging
・Firebase Predictions
・Firebase Dynamic Links
・Firebase App Indexing
・Firebase A/B Testing

本プロジェクトではGCPも併用して活用しています。
次回はGCPのゲーム活用事例を書きたい思います。

明日は@honeniqさんの記事です
最終日の記事も楽しみですね!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした