ゲームの統計画面の作り方:プレイ時間・回数・記録
遊びをつくる執筆にClaudeを使用しています。

ゲームに統計画面を付けると、プレイヤーは自分の歩みを振り返れるようになります。「合計○回プレイ」「最長プレイ時間○分」「ハイスコア達成日」といった数字は、ゲーム内容そのものには触れないにもかかわらず、プレイヤーのモチベーションを維持する力を持っています。 統計画面の実装で迷う点は「何を測るか」「どこに保存するか」「どう表示するか」の三つに集約されます。この記事ではその判断基準と具体的な手順を順に解説します。セーブデータの基礎的な読み書き方法には触れず、「統計」という目的に固有の設計と注意点に絞ります。
この記事の目次
統計データとして測る価値がある項目を選ぶ
統計画面の失敗例として多いのは「取れるものを全部並べた結果、何も伝わらない画面になる」パターンです。表示項目を選ぶ前に、「その数字を見たプレイヤーが次にどう行動するか」を一つ想像してみてください。
一般的に有用な統計項目は三種類に分けられます。
累積系(合計プレイ回数、合計プレイ時間、合計クリア数など)は、継続を実感させる数値です。1回ずつ積み上がるため実装がシンプルで、プレイヤーへの「あなたはこれだけ遊んだ」という証明になります。
最良記録系(ハイスコア、最短クリア時間、最高連続正解数など)は競争心を刺激します。自己記録の更新を検出する処理が必要になりますが、難しくはありません。
行動傾向系(最もよく選んだキャラクター、ステージ別クリア率、失敗した場所の集計など)はゲームデザインのフィードバックとしても使えます。ただし項目が増えると保存データのサイズが膨らみやすく、実装コストも上がります。最初は累積系と最良記録系の組み合わせから始め、ゲームの性質に応じて行動傾向系を追加するのが現実的な順序です。
プレイ時間と回数を正確に測る実装のポイント
プレイ時間はシンプルに見えて、測り方を間違えると実態とずれた数値になりやすい項目です。
よくある失敗は「ゲームを開いた時刻」から「ページを離れた時刻」を引くだけで計算することです。この方法だとプレイヤーがタブをそのままにして席を外した時間まで含まれます。実態に近いプレイ時間を測るには、次の考え方を組み合わせます。
- セッション開始時に Date.now() を記録する。
- 一定間隔(例:10秒ごと)に「最終アクティブ時刻」を更新する処理を入れる。
- セッション終了(ゲームオーバー・クリア・ページ離脱)時に、「最終アクティブ時刻 − セッション開始時刻」を加算する。
ページ離脱の検知には visibilitychange イベントや pagehide イベントを使います。ブラウザによって挙動に差があるため、両方を登録しておくと取りこぼしが減ります。
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
saveSessionTime(); // ここまでの時間を保存
}
});
window.addEventListener('pagehide', saveSessionTime);プレイ回数は「ゲーム開始ボタンを押したとき」または「ゲームオーバー・クリアになったとき」のどちらで加算するかを決めておく必要があります。「開始時」に加算すると途中離脱も含まれます。「終了時」に加算すると最後まで遊んだ試行だけをカウントできます。ゲームの性質(短期決着型か探索型か)に合わせて選んでください。
記録データの保存設計:ローカルとクラウドの使い分け
統計データをどこに保存するかは、ゲームの想定利用シーンによって異なります。
localStorage によるローカル保存は実装が最も手軽です。ブラウザに直接書き込むため、サーバーが不要で即座に読み書きできます。ただしブラウザのキャッシュ削除や別デバイスでのプレイで消えるリスクがあります。気軽に試してほしいゲームや、単体で完結するパズルゲームなどには十分な選択肢です。
保存する際はオブジェクトをまとめてJSON化する方法が管理しやすいです。
const stats = {
totalPlays: 0,
totalPlaySeconds: 0,
highScore: 0,
lastPlayedAt: null
};
localStorage.setItem('gameStats', JSON.stringify(stats));読み込む際は JSON.parse で戻しますが、データが存在しない初回起動のケースを必ず考慮してください。null をそのまま参照するとエラーになります。
クラウドへの保存を検討するのは、複数端末での継続プレイを想定する場合や、ランキング機能と統計を連動させたい場合です。アソバースでは作者がSDKを組み込むことで共通ログインとクラウドセーブに対応できます。クラウドセーブを使う場合は保存できるデータ量の上限を意識し、統計オブジェクトはできるだけ小さく設計するのが原則です。数値は整数に丸め、日付は Date.now() のUNIXタイムスタンプで持つとバイト数を抑えられます。
統計画面のUI設計:数字を「読める」形にする
数値をそのまま羅列しても伝わりません。統計画面のUIを設計する際に役立つ判断基準をまとめます。
単位と文脈を必ず添える。「1823」と表示するより「合計プレイ時間:30時間23分」と表示するほうが伝わります。秒数を時分秒に変換するユーティリティ関数を一つ作っておくと使い回せます。
function formatSeconds(totalSec) {
const h = Math.floor(totalSec / 3600);
const m = Math.floor((totalSec % 3600) / 60);
const s = totalSec % 60;
if (h > 0) return `${h}時間${m}分`;
if (m > 0) return `${m}分${s}秒`;
return `${s}秒`;
}初回プレイ時のゼロ表示をどう扱うかは見落とされがちです。「ハイスコア:0」はプレイ前のプレイヤーには不自然に見えます。未記録の場合は「−」や「まだ記録なし」と表示する分岐を入れてください。
記録更新の瞬間を祝うことも統計画面と密接に関係します。クリア後に「ハイスコア更新!」と表示する処理は、保存ロジックの中で「新しいスコア > 保存済みスコア」を比較するだけで実装できます。この瞬間がプレイヤーにとって統計画面を開く動機になります。
日付の表示は Date オブジェクトを使うと日本語形式に整形できます。タイムゾーンの扱いに注意が必要で、toLocaleDateString('ja-JP') を使うと環境に応じた日付文字列を得やすいです。
データ設計の失敗を防ぐチェックリスト
統計機能を後から修正すると、過去の保存データとの互換性問題が発生しやすくなります。公開前に次の点を確認しておくと後悔が減ります。
- 保存オブジェクトにバージョン番号(例:schemaVersion: 1)を持たせているか
- 読み込み時に項目が存在しない場合のデフォルト値を設定しているか
- 数値がNaNや負数になるエッジケースを検証したか(特に時間計測)
- プレイヤーがデータをリセットできる手段を提供しているか
- 保存データのキー名がほかのゲームと衝突しない命名になっているか(localStorage はドメイン内で共有されるため)
特に最後の点は複数ゲームを同一ドメインで公開する場合に見落とされます。gameStats_mygame_v1 のようにゲーム固有の識別子をキーに含める習慣をつけると安全です。
次の行動
まず「累積プレイ回数」と「最良スコア」の2項目だけを localStorage に保存し、クリア画面に表示する最小構成から試してみてください。動くものを作ってから項目を追加していくと、設計の迷いが減ります。アソバースへ投稿すれば実際のプレイヤーからの反応を見ながら統計機能を育てていけます。
参考資料
- MDN Web Docs(Web Storage API、Page Visibility API、pagehide イベント)
- MDN Web Docs(Date オブジェクト、toLocaleDateString)

