サーバー構築不要!スマートフォンアプリ向けの新クラウド

トップ >ドキュメント >チュートリアル(Unity):ハイスコアランキング機能を作る

チュートリアル(Unity)

ドキュメント内検索

ハイスコアランキング機能を作る

概要

このページでは、データストアに保存されたハイスコアを使って、ハイスコアランキングを実装する方法を学びます。ランキングは、全体の上位5件を表示したものと、自分の周囲だけを表示したものの2つを実装しましょう。下の画像が完成のイメージ図です。

完成イメージ図

なお、ログイン機能とハイスコアのサーバー管理機能を実装していない場合は、「ログイン機能を作る」「ハイスコアをサーバーに保存する」を先に済ませておいてください。

シーンファイルのダウンロード

ログイン画面実装時と同様に、今回もランキング表示画面とメインゲーム画面をそれぞれ別のシーンに分けて実装していきます。

下記リンクから、ランキング画面を実装するシーンファイルをダウンロードします。ダウンロードした「LeaderBoard.unity」を、プロジェクトの「Scenes」フォルダにドラッグしてコピーしてください。

LeaderBoardシーンのダウンロード

シーンのコピーが完了したら、Unityの File → Build Settingsを開き、今コピーしたシーンを「Scenes In Build」に追加(ドラッグ&ドロップ)してください。

このシーンには、既に以下のものが実装されています。

  • 各種見出しGUITextオブジェクト
  • 順位とプレイヤー名、スコアを表示するGUITextオブジェクト

このページでは、以下の内容を実装していきます。

  • メインシーン(Stage.unity)とランキングシーン(LeaderBoard.unity)を切り替える機能
  • mobile backendと通信してハイスコアを取得し、ランキングとして表示する機能

メインゲーム画面とランキング画面を切り替える

Manager.csを開き、OnGUIメソッド内を以下のように編集してください。

void OnGUI() {
    if( !IsPlaying() ){
      drawButton();

      // ランキングボタンが押されたら
      if ( leaderBoardButton )
        Application.LoadLevel("LeaderBoard");

      // これ以降はそのまま
      // ・・・
  }

これで、タイトル画面の「Leader Board」ボタンを押すとランキングシーンが表示されるようになります(ただし中身は空の状態)。

mobile backendに接続してランキングを取得する機能を作る

つづいて、mobile backendからハイスコアを取得して、ランキングデータを作る機能を実装しましょう。
HighScoreクラスと同様、今回のクラスもMVCデザインでいうモデルクラスにあたるので、Unityに依存しない形でコードを書き、ゲームオブジェクトにアタッチしないで使います。

Scriptsフォルダの中に「LeaderBoard.cs」という名前のC#スクリプトを新規作成し、下記の内容を記述してください。

using NCMB;
using System.Collections.Generic;

public class LeaderBoard {

  public int currentRank = 0;
  public List<NCMB.HighScore> topRankers = null;
  public List<NCMB.HighScore> neighbors  = null;

  // 現プレイヤーのハイスコアを受けとってランクを取得 ---------------
  public void fetchRank( int currentScore )
  {
    // データスコアの「HighScore」から検索
    NCMBQuery<NCMBObject> rankQuery = new NCMBQuery<NCMBObject> ("HighScore");
    rankQuery.WhereGreaterThan("Score", currentScore);
    rankQuery.CountAsync((int count , NCMBException e )=>{

    if(e != null){
      //件数取得失敗
    }else{
      //件数取得成功
      currentRank = count+1; // 自分よりスコアが上の人がn人いたら自分はn+1位
    }
      });
  }

  // サーバーからトップ5を取得 ---------------    
  public void fetchTopRankers()
  {
    // データストアの「HighScore」クラスから検索
    NCMBQuery<NCMBObject> query = new NCMBQuery<NCMBObject> ("HighScore");
    query.OrderByDescending ("Score");
    query.Limit = 5;
    query.FindAsync ((List<NCMBObject> objList ,NCMBException e) => {

    if (e != null) {
      //検索失敗時の処理
    } else {
      //検索成功時の処理
      List<NCMB.HighScore> list = new List<NCMB.HighScore>();
      // 取得したレコードをHighScoreクラスとして保存
      foreach (NCMBObject obj in objList) {
        int    s = System.Convert.ToInt32(obj["Score"]);
        string n = System.Convert.ToString(obj["Name"]);
        list.Add( new HighScore( s, n ) );
      }
      topRankers = list;
    }
      });
  }

  // サーバーからrankの前後2件を取得 ---------------
  public void fetchNeighbors()
  {
    neighbors = new List<NCMB.HighScore>();

    // スキップする数を決める(ただし自分が1位か2位のときは調整する)
    int numSkip = currentRank - 3;
    if(numSkip < 0) numSkip = 0;

    // データストアの「HighScore」クラスから検索
    NCMBQuery<NCMBObject> query = new NCMBQuery<NCMBObject> ("HighScore");
    query.OrderByDescending ("Score");
    query.Skip  = numSkip;
    query.Limit = 5;
    query.FindAsync ((List<NCMBObject> objList ,NCMBException e) => {

    if (e != null) {
      //検索失敗時の処理
    } else {
      //検索成功時の処理
      List<NCMB.HighScore> list = new List<NCMB.HighScore>();
      // 取得したレコードをHighScoreクラスとして保存
      foreach (NCMBObject obj in objList) {
        int    s = System.Convert.ToInt32(obj["Score"]);
        string n = System.Convert.ToString(obj["Name"]);
        list.Add( new HighScore( s, n ) );
      }
      neighbors = list;
    }
      });
  }
}

順に解説します。まずfetchRankメソッドでは、現在の自分のハイスコア「より大きい」スコアを持つレコードをWhereGreaterThanメソッドで検索し、CountAsyncメソッドでその数を取得しています。たとえば自分より大きいスコアが3人いたら、自分は4位だということがわかります。

次にfetchTopRankersメソッドでは、OrderByDescendingメソッドでスコアを降順にしつつ、Limitプロパティを5に設定することで、スコアの高い上位5人だけを取得しています。取得した5件のレコードをHighScoreクラスのListとして保存します。

fetchNeighborsメソッドでは、先述した降順並び替えとLimitの設定に加えて、現在の自分の順位によってSkipプロパティを設定することで、自分の周囲5件だけを取得しています。このように、SkipやLimit、ソートの指定によって、様々なデータの取得が可能です。詳しくはSDKリファレンスをご覧下さい。

HighScoreクラスを拡張する

今回はランキングを、「HighScoreクラスを5つ含んだList」という形で実装しました。そこで、HighScoreクラスに「自分のプレイヤー名とハイスコア」を文字列として出力させるメソッドを追加しましょう。

HighScore.csを開き、以下のメソッドを追加してください。

// ランキングで表示するために文字列を整形 -----------
public string print()
{
   return name + ' ' + score;
}

ランキングを画面に表示する機能の実装

最後に、ランキング画面に遷移したらランキングの取得を開始し、読み込みが完了したら画面に表示する機能を実装しましょう。

LeaderBoardシーンに「LeaderBoardManager」という名前で空のオブジェクトを作成し、同名のスクリプトを新規作成・アタッチしてください。その後、スクリプトを以下の内容に編集してください。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class LeaderBoardManager : MonoBehaviour {

  private LeaderBoard lBoard;
  private NCMB.HighScore currentHighScore;
  public GameObject[] top = new GameObject[5];
  public GameObject[] nei = new GameObject[5];

  bool isScoreFetched;
  bool isRankFetched;
  bool isLeaderBoardFetched;

  // ボタンが押されると対応する変数がtrueになる
  private bool backButton;

  void Start ()
  {
    lBoard = new LeaderBoard();

    // テキストを表示するゲームオブジェクトを取得
    for( int i = 0; i < 5; ++i ) {
      top[i] = GameObject.Find ("Top" + i);
      nei[i] = GameObject.Find ("Neighbor" + i);
    }

    // フラグ初期化
    isScoreFetched = false;
    isRankFetched  = false;
    isLeaderBoardFetched = false;

    // 現在のハイスコアを取得
    string name = FindObjectOfType<UserAuth>().currentPlayer();
    currentHighScore = new NCMB.HighScore( -1, name );
    currentHighScore.fetch();
  }

  void Update()
  {
    // 現在のハイスコアの取得が完了したら1度だけ実行
    if( currentHighScore.score != -1 && !isScoreFetched ){  
      lBoard.fetchRank( currentHighScore.score );
      isScoreFetched = true;
    }

    // 現在の順位の取得が完了したら1度だけ実行
    if( lBoard.currentRank != 0 && !isRankFetched ){
      lBoard.fetchTopRankers();
      lBoard.fetchNeighbors();
      isRankFetched = true;
    }

    // ランキングの取得が完了したら1度だけ実行
    if( lBoard.topRankers != null && lBoard.neighbors != null && !isLeaderBoardFetched){ 
      // 自分が1位のときと2位のときだけ順位表示を調整
      int offset = 2;
      if(lBoard.currentRank == 1) offset = 0;
      if(lBoard.currentRank == 2) offset = 1;

      // 取得したトップ5ランキングを表示
      for( int i = 0; i < lBoard.topRankers.Count; ++i) {
    top[i].guiText.text = i+1 + ". " + lBoard.topRankers[i].print();
      }
    
      // 取得したライバルランキングを表示
      for( int i = 0; i < lBoard.neighbors.Count; ++i) {
    nei[i].guiText.text = lBoard.currentRank - offset + i + ". " + lBoard.neighbors[i].print();
      }
      isLeaderBoardFetched = true;
    }
  }

  void OnGUI () {
    drawMenu();
    // 戻るボタンが押されたら
    if( backButton )
      Application.LoadLevel("Stage");
  }

  private void drawMenu() {
    // ボタンの設置
    int btnW = 170, btnH = 30;
    GUI.skin.button.fontSize = 20;
    backButton = GUI.Button( new Rect(Screen.width*1/2 - btnW*1/2, Screen.height*7/8 - btnH*1/2, btnW, btnH), "Back" );
  }
}

Startメソッドでハイスコアの取得を開始し、ハイスコアの取得が終わったら自分の順位の取得を開始し、それが終わったらランキングの取得を開始しはじめます。それぞれの取得はAsync系のメソッド、つまり非同期に行われるので、処理の終了をフラグで判断し、同期処理化しています。ランキングの取得まで終わったら、GUITextに順位とプレイヤー名、スコアを表示しています。

OnGUIとdrawMenuメソッドでは、いつものようにボタンの描画と画面遷移を行っています。

動作確認

おつかれさまでした!以上でハイスコアランキング機能の実装が完了しました。LogInシーンを開いてゲームを実行し、ランキングが表示されることを確かめましょう。事前に5人以上プレイヤーを作ってスコアを登録しておくとよいでしょう。

  • ボタンを押すことでランキング画面とメインゲーム画面を移動できる
  • トップ5のスコアが表示されている
  • 自分の前後2件のスコアが表示されている

お探しの内容が見つからなかった場合はユーザーコミュニティ もご活用ください。(回答保証はいたしかねます)
なお、 Expertプラン以上のお客様はテクニカルサポートにてご質問を承らせて頂きます。

推奨画面サイズ1024×768px以上

ページの先頭へ