ブラウザゲームを作ってみよう(その12:サンプルゲーム作成その5)

# はじめに
[その11](https://qiita.com/noji505/items/e9a53c81de36f34138d4)ではオプションモードの追加を行いました。
今回はランキングを追加してみたいと思います。

# ランキングについて
タイトル画面からランキング画面へ入れるようにし、そこでランキングが確認出来るようにします。
基本的には前回のオプション画面の追加と同じイメージになります。

[遷移イメージ]
![img01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/740996/71f61ca4-8881-efee-0f33-71fe665f7e22.png)

ランキングの保存についてはLocal Storageを使用します。

# Local Storageの使い方
基本的にはデータの保存、取得、削除の3種類を覚えれば扱えます。

<b>保存</b>
localStorage.setItem( 'key', 'val' )か、localStorage.key= 'val' で保存できます。

```javascript:localStorage.js
localStorage.setItem( 'key', 'val' );
localStorage.saveKey = 'val';
```

<b>取得</b>
localStorage.getItem( 'key' )か、localStorage.key で値を取得できます。

```javascript:localStorage.js
var val1 = localStorage.getItem( 'key' );
var val2 = localStorage.key;
```

<b>削除</b>
localStorage.removeItem( 'key' )で特定のキーの値を削除、
localStorage.clear()で全てのデータを削除できます。

```javascript:localStorage.js
localStorage.removeItem( 'key' );
localStorage.clear();
```

# ソースコード

まずはゲーム起動時にローカルストレージの初期化とランキングデータの読み込みを行う処理を追加します。
ソースコードは以下となります。

```javascript:ローカルストレージ初期化、保存、読み込み

// 変数
var localScores = new Array();
var localRankinPos;     // ゲーム後のローカルランキングの順位(1以上:順位、-1:ランク外、-2:オプション設定が初期値ではないため保存無し)

function init(){
{
    // ローカルランキングスコア取得
    localScores = localStorage.scores;
    if( localScores == null ){
        // 保存されていない場合は初期化して保存
        localScores = new Array( 5 );
        for( var i=0; i<localScores.length; i++ ){
            localScores[i] = 0;
        }
        localStorage.scores = localScores;
    } else {
        localScores = localScores.split( ',' );
    }
}
```

■実装ポイント
* ローカルストレージから読み込みを行い、データが無い場合は初期値をセットします
* データが既にある場合は変数にセットします
<font color="Red">※配列で保存していますが、実際はローカルストレージ内部は文字列なので読み込み後は配列変数に変換する必要があります</font>

次に、タイトル画面のソースコードは以下となります。

```javascript:タイトル画面

// タイトル画面ループ
function titleMainLoop()
{
    drawText( "コインゲット", 0, 20, '#000000', 12, TEXT_HCENTER );


    drawText( "ゲーム開始", 0, SC_H - 100, '#000000', 12, TEXT_HCENTER );
    drawText( "オプション", 0, SC_H - 70, '#000000', 12, TEXT_HCENTER );
    drawText( "ローカルランキング", 0, SC_H - 40, '#000000', 12, TEXT_HCENTER );

    if( keyTrg == KEY_UP ){
        cursorNum--;
        if( cursorNum < 0 ) cursorNum = 0;
    }
    if( keyTrg == KEY_DOWN ){
        cursorNum++;
        if( cursorNum > 2 ) cursorNum = 2;
    }
    // 選択カーソル表示
    if( cursorNum == 0 ){
        drawFill( 100, SC_H - 98, 12, 12, 0x0000FF, null );
        drawFill( 188, SC_H - 98, 12, 12, 0x0000FF, null );

    } else if( cursorNum == 1 ){
        drawFill( 100, SC_H - 68, 12, 12, 0x0000FF, null );
        drawFill( 188, SC_H - 68, 12, 12, 0x0000FF, null );

    } else if( cursorNum == 2 ){
        drawFill( 78, SC_H - 38, 12, 12, 0x0000FF, null );
        drawFill( 208, SC_H - 38, 12, 12, 0x0000FF, null );

    }

    if( keyTrg == KEY_SPACE ){
        if( cursorNum == 0 ){
            gameInit();  // ゲーム画面初期化へ
        } else if( cursorNum == 1 ){
            optionInit();  // オプション画面初期化へ
        } else if( cursorNum == 2 ){
            localRankingInit();  // ローカルランキング画面初期化へ
        }
    }
}
```

■ポイント
* 基本的には前回のオプション画面追加と同様の修正になります。

次に、ランキング画面のソースコードは以下となります。

```javascript:ランキング画面
// ローカルランキング画面ループ
function localRankingMainLoop()
{
    drawText( "ローカルランキング", 0, 15, '#000000', 16, TEXT_HCENTER );

	for( var i=0; i<localScores.length; i++ ){
		drawText( ""+(i+1)+"位", (SC_W >> 1) - 100, 50+(i*30), '#000000', 12, TEXT_NONE );
		drawText( ""+localScores[i]+"", (SC_W >> 1) + 50, 50+(i*30), '#000000', 12, TEXT_NONE );
	}

	if( keyTrg == KEY_SPACE ){
        titleInit();	// タイトル初期化へ
	}
}
```

■ポイント
* 「localScores」変数に5件のランキングデータが入っているので、それを順番に表示しています。

次に、ランキングデータの反映、結果表示になります。

```javascript:ランキングデータ反映、結果表示

// ゲーム結果画面初期化
function resultInit()
{
    localRankinPos = -2;

    if( updateLife == initLife &&
        updateCoinChangeScore == initCoinChangeScore &&
        updateCoinShowNumScore == initCoinShowNumScore &&
        updateCoinShowNum == initCoinShowNum ){

            localRankinPos = saveLocalRanking( score );
    }

    gameState = 2;    // ゲーム結果画面ループへ
}

// ゲーム結果画面ループ
function resultMainLoop()
{
    // ローカルランキングのランクイン状態表示
    if( localRankinPos == -2 ){
        drawText( "オプションが変更されているためランキング保存されません", 0, 70, '#000000', 10, TEXT_HCENTER );

    } else if( localRankinPos == -1 ){
        drawText( "ランク外でした", 0, 70, '#000000', 10, TEXT_HCENTER );

    } else {
        drawText( "「"+localRankinPos+"位」にランクインしました", 0, 70, '#000000', 10, TEXT_HCENTER );

    }
}

//-----------------------------------------------------------
//    saveLocalRanking : ローカルランキング保存
//                引数 : 保存データ
//              戻り値 : データ保存位置(-1で保存無し)
//-----------------------------------------------------------
function saveLocalRanking( saveData )
{
	var savePos = -1;		// データ保存位置
	var i;

	// データの挿入位置をチェック
	for( i=0; i<localScores.length; i++ ){
		if( saveData > localScores[i] ){
			savePos = i;
			break;
		}
	}

	if( savePos != -1 ){	// ランクインした時のみローカルストレージに保存
		for( i=localScores.length-1; i>savePos; i-- ){
			localScores[i] = localScores[i-1];	// データを1つずらす
		}
		localScores[savePos] = saveData;
        localStorage.scores = localScores;
		savePos++;
	}

	return savePos;
}
```

■ポイント
* オプション設定が変更されていない場合のみ、ランキングデータを反映します
* 結果画面でランクインしたかどうか？の表示を行います

# サンプル更新

今回の変更点を全て反映したサンプルは以下になります。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="oNqdRVq" data-user="noji505" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/noji505/pen/oNqdRVq">
  Qiita23_sample01</a> by nojima (<a href="https://codepen.io/noji505">@noji505</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

ランキングが保存されるか試してみて下さい。

# 最後に
今回はローカルストレージを利用してランキングを保存出来るようにしました。
ローカルストレージはWebシステムでも利用出来るシーンはあると思うので、知っておいて損はないと思います。