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

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

Unity×NCMB。Unityゲームにランキング機能を追加しよう

最初のユーザ登録前回のハイスコアの記録に続いて最後にランキング表示機能を追加します。

シーンのダウンロード

チュートリアルに公開しているLeaderBoardをダウンロード&解凍します。解凍したら、LeaderBoard.unityを Assets/2dspaceshooter/Scenes/ にドラッグ&ドロップしてください。

シーンに追加したら、 FileメニューのBuild Settingsを開いてLeaderBoardを追加します。

LeaderBoardシーンの編集

LeaderBoardシーンに切り替えます。そしてHierarchyにあるBackgroundsを選択して削除します。

さらにmenuシーンにあるStarsをコピーしてLeaderBoardに貼り付けます。

後はGUI Textのフォントをscoreにしておきます。Main Cameraに背景色がついているので黒にしましょう。

LeaderBoardの作成

Assets/2dspaceshooter/Scripts/ の中に LeaderBoard というC#スクリプトを作成します。内容は次の通りです。

using NCMB;
using System.Collections.Generic;

public class LeaderBoard {
    
    public int currentRank = 0;
    public List<HighScore> topRankers = null;
    public List<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<HighScore> list = new List<HighScore>();
                // 取得したレコードをHighScoreクラスとして保存
                foreach (NCMBObject obj in objList) {
                    int    s = System.Convert.ToInt32(obj["Score"]);
                    string n = System.Convert.ToString(obj["Name"]);
                    HighScore h = new HighScore();
                    h.username = n;
                    h.score = s;
                    list.Add( h );
                }
                topRankers = list;
            }
        });
    }
    
    // サーバーからrankの前後2件を取得 ---------------
    public void fetchNeighbors()
    {
        neighbors = new List<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<HighScore> list = new List<HighScore>();
                // 取得したレコードをHighScoreクラスとして保存
                foreach (NCMBObject obj in objList) {
                    int    s = System.Convert.ToInt32(obj["Score"]);
                    string n = System.Convert.ToString(obj["Name"]);

                    HighScore h = new HighScore();
                    h.username = n;
                    h.score = s;
                    list.Add( h );
                }
                neighbors = list;
            }
        });
    }
}

内容はランクの取得、トップ5の取得、そして自分の前後2位の取得となっています。

ランキングの表示処理

LeaderBoardシーンに空のGameObjectを作成し、名前を LeaderBoardManager とします。さらに同名の C#スクリプトを作成し、次の内容にした上で GameObject にアタッチします。

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

public class LeaderBoardManager : MonoBehaviour {
    
    private LeaderBoard lBoard;
    private 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;
        
        // 現在のハイスコアを取得
        if (FindObjectOfType<UserAuth> () != null) {
            string name = FindObjectOfType<UserAuth> ().currentPlayer ();
            currentHighScore = this.gameObject.AddComponent<HighScore>();
            currentHighScore.score = -1;
            currentHighScore.username = name;
            currentHighScore.fetch ();
        }
    }
    
    void Update()
    {
        // 現在のハイスコアの取得が完了したら1度だけ実行
        if( currentHighScore != null && 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("menu");
    }
    
    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" );
    }
}

menuシーンからLeaderBoardへ移るボタンを作成します。

menuシーンに切り替えて、GUI Textを作成します。ラベルはRankingとします。

Inspectorは次のようになります。

  • Position : X 0.44 / Y 0.15 / Z 0
  • Text : Ranking
  • Font : score
  • Font Size : 30

このような表示になります。

続いて Assets/2dspaceshooter/Scenes/menu.js を開きます。

var ranking:GUIText;
  : 
if( Input.GetMouseButtonDown(0)){
    if( logoutButton.guiText.HitTest(Input.mousePosition) ) {
        FindObjectOfType(UserAuth).logOut();    
        Application.LoadLevel("LogIn");
    }
    // 追加ここから
    if( ranking.guiText.HitTest(Input.mousePosition) ) {
        Application.LoadLevel("LeaderBoard");
    }
    // 追加ここまで
}

としてシーン切り替えを行います。

最後にGUIのInspectorにあるrankingに対してGUI TextのRankingをアタッチして完成です。

動かしてみる

menuシーンを再生すると、Rankingのラベルが表示されるようになります。

ただしランキングが表示されるのはログインしてからなので、まずログインをします。

ログインしたらRankingをクリックしてみましょう。ランキングが表示されれば完成です!


いかがでしたか。チュートリアルに登録されているシーンと、今回提示したコードを真似ることで自作のゲームにも簡単にログイン、ランキングと言った機能が追加できるようになります。ゲームをより盛り上げるためにもぜひ参考にしてください!