ニフクラ mobile backend(mBaaS)お役立ちブログ

スマホアプリ開発にニフクラ mobile backend(mBaaS)。アプリ開発に役立つ情報をおとどけ!

プログラミング学習環境「Monaca Education」で話題のぷよぷよプログラミングにランキング機能をつけよう!【前編】

f:id:mbaasdevrel:20200818150620p:plain

本記事は国産のモバイルアプリ開発ツール「Monaca Education」でぷよぷよをプログラミングできる「ぷよぷよプログラミング」に、ランキング機能を追加実装するコンテンツとなります。

「ぷよぷよプログラミング」は、プログラミングを学ぶ上での基本である写経を徹底するスタイルで、かなりストイックなチャレンジとなっています。ぜひ皆さんもぷよぷよプログラミングにチャレンジしてみてはいかがでしょうか。

※本記事執筆にあたり、Monaca(アシアル社)とセガ社の許可をいただいています。

ベースについて

ベースになるプログラムは「ぷよぷよプログラミング」サポートページ | Monaca Educationを実装し終えたプロジェクトになります。 なお、実装済みであれば、レベルは「初級」「中級」「上級」のどれでも構いません。

ニフクラ mobile backendを導入する

ニフクラ mobile backend(NCMB)を導入する流れについてです。まずはユーザ登録してください(無料です)。

ニフクラ mobile backend

ニフクラ mobile backendを使えば、データベース、ファイルストレージ、会員管理、プッシュ通知等、スマートフォンアプリのバックエンド機能をカンタンに実装することができます!今回はニフクラ mobile backendを使ってランキング機能を実装します。

f:id:mbaasblog:20200610115326p:plain

ユーザ登録を終えたら、最初にアプリを作ります。アプリ名は puyopuyo など分かりやすいものしておきましょう。 アプリケーションキーとクライアントキーという二つのキーが得られます。どちらもメモしておいてください。

f:id:mbaasdevrel:20200818150058p:plain

匿名認証を有効にする

今回は得点を記録する際に、他の人に誤って削除されたり操作されないように、ACL(アクセス制限)を設けます。ID/パスワードを使った認証もできますが、わざわざ登録するのは面倒です。そこで、そういった手間なく使える匿名認証機能を利用します。

そのために、ニフクラ mobile backendの管理画面にて、アプリ設定の中にある匿名設定を有効にします。

f:id:mbaasdevrel:20200825104733p:plain

Monacaでの操作

次にMonacaにログインして、ぷよぷよプログラミングのプロジェクトをWeb IDEで開きます。そして設定メニューの中にあるJS/CSSコンポーネントの追加と削除を選択します。

f:id:mbaasdevrel:20200818150116p:plain

出てきた画面でコンポーネント名としてNCMBと入力して、検索するボタンをクリックします。そうすると ncmb が表示されますので、右側にある追加ボタンをクリックします。

f:id:mbaasdevrel:20200818150132p:plain

ダイアログが出ますので、そのままインストールボタンを押してください。

f:id:mbaasdevrel:20200818150206p:plain

読み込むファイルを選択してくださいというダイアログで components/ncmb/ncmb.min.js を忘れずにチェックしてください。チェックしたら、保存ボタンを押します。これでNCMBのインストールが完了しました。

NCMBの初期化

www/src/game.js を開きます。1行目から、次のように記述してください。アプリケーションキーとクライアントキーは、それぞれ取得したものを指定してください。

// アプリケーションキーとクライアントキー。NCMBから取得して記述してください。
const applicationKey = '70d...934';
const clientKey = '4d0...dff';
// NCMBを初期化する
const ncmb = new NCMB(applicationKey, clientKey);

これで初期化が終了です。

匿名認証を使う

次に得点データを保存するにあたり、前述の通り匿名認証を利用します。まず function initialize を探して、次のように async を追加してください。

// 変更前
function initialize() {

// 変更後
async function initialize() {

このようにasyncをつけることで、async/await処理が使えるようになります。

次に frame = 0; の下に次のように記述を追加します。内容はコメントを参照してください。ネットワークがオフラインの場合なども考えられますが、今回は省略します。

// 匿名認証を行う
// エラーがあった場合(セッションが切れている場合など)に備えて try/catchを使います
try {
    // 現在ログイン中かどうか調べます
    const currentUser = ncmb.User.getCurrentUser();
    if (currentUser) {
        // ログイン中の場合はセッションの有効性を確認します
        // 自分自身の情報が取れるか確認します
        await ncmb.User.equalTo('objectId', currentUser.get('objectId')).fetch();
    } else { 
        // ログインしていない場合は匿名認証を実行します
        await ncmb.User.loginAsAnonymous();
    }
} catch (e) {
    // エラーが起きたらここに移動します(セッションが切れている場合など)
    // エラーの場合は強制ログアウトします
    ncmb.sessionToken = null;
    // そして再度匿名認証を実行します
    await ncmb.User.loginAsAnonymous();
}

これで匿名認証が完了しました。利用者は会員登録のような面倒な処理を行うことなく、ACL(アクセス制限)を使った安全なデータ利用が可能になります。

もしここでエラーが出る場合、ニフクラ mobile backendの設定で匿名認証が有効になっていない可能性があります。管理画面にログインして、アプリ設定を確認してください。

得点を残す

ではいよいよ得点を残す機能について解説します。この処理は initialize 関数の下にある loop 関数の中に実装します。まず loop 関数にもasyncを追加します。

// 変更前
function loop() {

// 変更後
async function loop() {

次に case 'gameOver': というコードを探してください。ここがゲームオーバーになったタイミングで呼び出される部分になります。この中に得点を保存する機能を追加していきます。

case 'gameOver':
    // ばたんきゅーの準備をする
    PuyoImage.prepareBatankyu(frame);
    mode = 'batankyu';
    // この下にコードを記述していきます
    break;

得点を取得する

まず得点を取得します。これは Score.score で取得できます。そして、その得点が0点以上であれば、処理を続けて書いていきます。

const score = Score.score;
if (score > 0) {
  // 以下はこの中に記述していきます
}

プレイヤーの名前を取得する

得点を保存する際に必要なプレイヤーの名前を取得します。これはNCMBの匿名認証に紐付けます。初回時はプロンプトを出してユーザに入力してもらいます。NCMBでは set メソッドを使って情報を追加できます。そして、更新する際には update メソッドを使います。

update を呼ぶ際に、await を使えば更新処理が完了するまで待つことになります。今回の場合、待たずに処理を進めて問題ないので、awaitは使いません。

// 名前を取得
const player = ncmb.User.getCurrentUser();
// 名前がすでに登録されている場合はそれを、なければプロンプトで入力してもらいます
const playerName = player.get('name') || prompt(`${score}点を取りました。記録する場合は名前を入力してください`);
if (!playerName) break; // 入力していなかったら終了
// 入力した名前を保存しておく
player.set('name', playerName).update(); // 処理確認を待つ必要はないので先に進める

f:id:mbaasdevrel:20200818150444p:plain

得点データを作成する

次に得点データを作ります。データストアのクラス名は自由に決められます。今回はRankingクラスをしました。

const Ranking = ncmb.DataStore('Ranking');
const ranking = new Ranking;

権限を設定する

得点を保存する際には、適切なACLを設定します。今回の場合は、読み取りは誰でも可能、更新削除は不可としています。そうすることで、他ユーザとのランキング比較、そして不正なデータ改ざんを防止できます。

更新および削除はプレイヤー自身のみ可能としています。つまりここで匿名認証が活きてきます。

// 権限を設定する
const acl = new ncmb.Acl;
acl
    .setUserWriteAccess(player, true) // ユーザは書き込み(更新)可能
    .setPublicReadAccess(true);       // 誰でも読み込み可能

得点を保存する

そしてACL、プレイヤーの名前、得点を保存します。

await ranking
    .set('playerName', playerName)
    .set('score', score)
    .set('acl', acl)
    .save();

なお、ここでの問題として、任意のスコアを保存できてしまうというのが挙げられます。コードを改ざんすることで自由に得点を保存できてしまうということです。今回は簡易的なものなので、そうした制御は行っていませんが、スクリプト機能を使ってコードを隠蔽化すれば防止できるでしょう。

ランキングを取得する

得点を保存したら、順位を取得してみましょう。これは、保存した得点よりも高いデータが何件あるか調べれば分かります。つまり、次のように実装できます。

// 順位を調べる
const result = await Ranking
    .greaterThan('score', score) // 〜より大きいという条件指定
    .count()                     // 件数を調べる際の指定
    .fetchAll();                 // データを取得
const number = result.count + 1;
alert(`${playerName}さん、おめでとうございます!ランキングは${number}位です!`);

これだけで順位が分かります。今回は同一得点の場合、同一順位としています。

f:id:mbaasdevrel:20200818150545p:plain

全体のコード

game.jsに行った修正の中で一番長い、ゲームオーバー時のコードは次のようになります。参考にしてください。

case 'gameOver':
    // ばたんきゅーの準備をする
    PuyoImage.prepareBatankyu(frame);
    mode = 'batankyu';
    const score = Score.score;
    if (score > 0) {
        // 名前を取得
        const player = ncmb.User.getCurrentUser();
        const playerName = player.get('name') || prompt(`${score}点を取りました。記録する場合は名前を入力してください`);
        if (!playerName) break; // 入力していなかったら終了
        // 入力した名前を保存しておく
        player.set('name', playerName).update(); // 処理確認を待つ必要はないので先に進める
        const Ranking = ncmb.DataStore('Ranking');
        const ranking = new Ranking;
        // 権限を設定する
        const acl = new ncmb.Acl;
        acl
            .setUserWriteAccess(player, true)
            .setPublicReadAccess(true);
        await ranking
            .set('playerName', playerName)
            .set('score', score)
            .set('acl', acl)
            .save();
        // 順位を調べる
        const result = await Ranking
            .greaterThan('score', score)
            .count()
            .fetchAll();
        const number = result.count + 1;
        alert(`${playerName}さん、おめでとうございます!ランキングは${number}位です!`);
    }
    break;

まとめ

今回はランキング機能を実装するにあたって、次の2つの機能を使いました。

  • 会員管理(匿名認証)
  • データストア(ランキングデータ保存、取得)

権限管理を行うことで、クラウド上にあるデータを安全に使えるようになります。

次回、ランキングの表示機能を作ってみたいと思います。

中津川 篤司

中津川 篤司

NCMBエヴァンジェリスト。プログラマ、エンジニアとしていくつかの企業で働き、28歳のときに独立。 2004年、まだ情報が少なかったオープンソースソフトの技術ブログ「MOONGIFT」を開設し、毎日情報を発信している。2013年に法人化、ビジネスとエンジニアを結ぶDXエージェンシー「DevRel」活動をスタート。