VRレーシングゲームのゴールタイムをサーバーからDLして、ランキング形式に表示させる

[この記事](http://qiita.com/fumishitan/items/2b60f27b79fdb4772be4)の８回目。

今回は[前回の記事](http://qiita.com/fumishitan/items/3367eca029da9a251a57)でサーバーに保存したスコアを引き下ろして、ランキング表示させます。ちなみにサーバーは[ニフティクラウド mobile backend](http://mb.cloud.nifty.com/)を利用しています。
やることは３つです。

・データを一時的に持つクラスの作成
・サーバーからデータを引き下ろす
・データを一覧で表示する

## データを一時的に持つクラスの作成

下記のようなスクリプトを作った
今回の場合だとあまり必要ないが、今後名前など、スコア以外の別データを紐づけることを考えると下記のようなものがあるといいと思う

```csharp:Rankers.cs

using UnityEngine;
using System.Collections;

namespace NCMB
{
	
	public class Rankers
	{
		
		public string time   { get; set; }
		
		// コンストラクタ -----------------------------------
		public Rankers(string _time)
		{
			time = _time;
		}
		
		// ランキングで表示するために文字列を整形 -----------
		public string print()
		{
			return time;
		}
	}
	
}

```

## サーバーからデータを引き下ろす

[ニフティクラウド mobile backend](http://mb.cloud.nifty.com/)以下（NCMB）ではSDKが提供されているのでそれを利用し下記のようにデータを引き出せます。

```csharp:LeaderBoard.cs

using NCMB;
using System.Collections;
using System.Collections.Generic;

public class LeaderBoard {
	
	public int currentRank = 0;
	public List<NCMB.Rankers> topRankers = null;
	
	// サーバーからトップ5を取得 ---------------    
	public void fetchTopRankers()
	{
		// データストアの「Time」クラスから検索
		NCMBQuery<NCMBObject> query = new NCMBQuery<NCMBObject> ("Time");
		query.OrderByAscending ("time");
		query.Limit = 5;
		
		query.FindAsync ((List<NCMBObject> objList ,NCMBException e) => {
			
			if (e != null) {
				//検索失敗時の処理
			} else {
				//検索成功時の処理
				List<NCMB.Rankers> list = new List<NCMB.Rankers>();
				// 取得したレコードをtimeクラスとして保存
				foreach (NCMBObject obj in objList) {
					string    t = System.Convert.ToString(obj["time"]);
					list.Add( new Rankers(t) );
				}
				topRankers = list;
			}
		});
	}
	
}

```

少し解説を交えると、NCMBではデーターベースを(検索などの)操作するときにクエリという命令を投げる形になっています。まずどのクラスへの操作なのかを指定します。

``` csharp:

NCMBQuery<NCMBObject> query = new NCMBQuery<NCMBObject> ("Time");
```

次に、time カラムを降順にならびかえる

``` csharp:

query.OrderByAscending ("time");
```

そして一番、小さいものから順に５個とってくる

```csharp

query.Limit = 5;
```

最後のquery.FindAsyncで非同期でデータを取ってくる

## データを一覧で表示する

後は一覧表示の部分です。今回はTimer.csのOnGUIの部分で描画するようにします。
それが下記のような形になります。

```csharp:

if( !isRankFetched ){
	lBoard.fetchTopRankers();
	isRankFetched = true;
}
				
// ランキングを描画
if( lBoard.topRankers != null ){
	GUI.Label (new Rect (goal_x-goal_w/2, 15, goal_w, goal_h),"Top Ranking");
	for( int i = 0; i < lBoard.topRankers.Count; ++i) {
		GUI.Label (new Rect (goal_x-goal_w/2, 10 + goal_y*(i+1)/2, goal_w, goal_h),"Time"+ (i+1) + "."+ lBoard.topRankers[i].print());
	}
}

```

前回のゴールタイムの表示部分に「RankingButton」を作り、そこから遷移して描画するようにすると下記のようになる。

```csharp:Timer.cs

using UnityEngine;
using System.Collections;
using NCMB;

public class Timer : MonoBehaviour {
	float startTime;
	float lapTime;
	bool goal = false;
	bool ranking = false;
	private LeaderBoard lBoard;
	bool isRankFetched;
	
	
	void Start () {
		startTime = Time.time;
		lBoard = new LeaderBoard();
		// フラグ初期化
		isRankFetched  = false;
	}
	
	
	void Update () {
		if (goal == false) {
			// 現在の経過時間を算出
			lapTime = Time.time - startTime;
		}
	}
	
	void OnGUI(){
		if (goal == false) {
			// 現在の経過時間を表示
			float timer_x = Screen.width * 8 / 10;
			float timer_y = Screen.height / 10;
			float timer_w = Screen.width * 3 / 10;
			float timer_h = Screen.height / 2;
			GUI.Label (new Rect (timer_x, timer_y, timer_w, timer_h), "Time" + lapTime.ToString ());
		} else {
			// ゴールタイムを表示
			float goal_x = Screen.width / 2;
			float goal_y = Screen.height / 5;
			float goal_w = Screen.width * 3/ 10;
			float goal_h = Screen.height / 10;
			if(ranking == false){
				GUI.Label (new Rect (goal_x - goal_w/2, goal_y, goal_w, goal_h), "Your Goal Time!");
				GUI.Label (new Rect (goal_x - goal_w/2, goal_y*2, goal_w, goal_h), lapTime.ToString ());
				if (GUI.Button(new Rect(goal_x - goal_w, goal_y*3, goal_w, goal_h), "ReStart")) {
					Application.LoadLevel ("run");
				}
				
				if(GUI.Button(new Rect(goal_x, goal_y*3, goal_w, goal_h), "Ranking")){
					ranking = true;
				}
				
			}else{
				// 現在の順位の取得が完了したら1度だけ実行
				if( !isRankFetched ){
					lBoard.fetchTopRankers();
					isRankFetched = true;
				}
				
				// ランキングを描画
				if( lBoard.topRankers != null ){
					GUI.Label (new Rect (goal_x-goal_w/2, 15, goal_w, goal_h),"Top Ranking");
					for( int i = 0; i < lBoard.topRankers.Count; ++i) {
						GUI.Label (new Rect (goal_x-goal_w/2, 10 + goal_y*(i+1)/2, goal_w, goal_h),"Time"+ (i+1) + "."+ lBoard.topRankers[i].print());
					}
				}
				
				if (GUI.Button(new Rect(goal_x - goal_w/2, 10 + goal_y*3, goal_w, goal_h), "Back")) {
					ranking = false;
				}
			}
		}
	}
	
	// Goal到着が検知されたとき
	void OnGoal(){
		goal = true;
		//クラスの指定
		NCMBObject timeClass = new NCMBObject("Time"); //カラムと挿入するデータの指定
		timeClass["time"] = lapTime; //非同期でのアップロード
		timeClass.SaveAsync();

	}
}

```

実際にこれらを使ってゲームをゴールすると

![スクリーンショット 2015-08-01 21.14.54.png](https://qiita-image-store.s3.amazonaws.com/0/55227/da316d2a-742f-1385-4424-1b0421fd76f8.png)

ゴール画面に、「Ranking」が表示され、ランキングを押すと

![スクリーンショット 2015-08-01 21.17.33.png](https://qiita-image-store.s3.amazonaws.com/0/55227/83820227-38eb-2d6b-c551-eea70bfc8bd4.png)

上記のようにランキングが表示されます。

これでランキングも完成ということで、、、しばらくVRゲームの作成編はこれにて一旦終了です。