マガジン

ゲームのローディング画面を作る:体感待ち時間を減らす

遊びをつくる

執筆にClaudeを使用しています。

異なる大きさの円を同じ進度で巡る玉で、時間に基づくゲームの動きを表したイラスト。
記事のテーマをもとに制作したAI生成イラスト。

ゲームを開いたとき、真っ白な画面が数秒続くだけでプレイヤーは「壊れているのかも」と感じます。実際の読み込み時間が同じでも、何かが動いている画面とそうでない画面では、後者のほうがずっと長く感じられます。この「体感待ち時間」を縮めることが、ローディング画面設計の核心です。 ローディング画面を作る目的は二つあります。一つは「ちゃんと動いている」という安心感を伝えること、もう一つはゲームの世界観を最初の数秒で予感させることです。スプラッシュ画像を一枚置くだけでも前者は達成できますが、後者まで意識すると、ローディング画面はゲームの入口として機能し始めます。 この記事では、HTMLとJavaScriptで動くブラウザゲームを対象に、ローディング画面の実装パターンと、体感待ち時間を縮めるための具体的な判断基準を説明します。

この記事の目次
  1. なぜ体感時間は実時間と食い違うのか
  2. 最初に作る最小構成
  3. プログレスバーを正直に動かす
  4. スマートフォンで特に注意すべき点
  5. 世界観を伝える演出の判断基準
  6. 次の行動

なぜ体感時間は実時間と食い違うのか

人がストレスを感じるのは「何も変化がない時間」です。プログレスバーが0%から止まっている状態と、アニメーションが動き続けている状態では、同じ3秒でも後者のほうが短く感じられます。これは心理学的に広く知られた現象であり、空港のエスカレーターや病院の待合室でも同じ工夫が使われています。

ブラウザゲームにおいても同じことが言えます。特にスマートフォンでは画像や音声ファイルの読み込みが回線状況によって大きく変わるため、実時間のばらつきは避けられません。しかし体感時間はコントロールできます。具体的には次の三つの要素が効きます。

  • フィードバックの有無:プログレスバー、スピナー、カウンターなど何らかの変化が起きていること
  • 変化の滑らかさ:ガクガクした更新より、CSSアニメーションで継続的に動いているほうが短く感じられること
  • 意味のある情報:「読み込み中…」より「マップを生成しています」など、何をしているかが伝わると待てること

この三つを設計方針にすることで、ローディング画面の実装方針が決まります。

最初に作る最小構成

まずゲーム全体を覆うローディング用のレイヤーを用意します。構造はシンプルで、ゲームのcanvasやdivの上に重なる全画面のdivを一枚置き、リソースの読み込みが終わったらそれを非表示にするだけです。

html
<div id="loading-screen">
  <div class="spinner"></div>
  <p id="loading-message">読み込み中...</p>
</div>
<canvas id="game-canvas"></canvas>

CSSでは#loading-screenにposition: fixed; inset: 0; z-index: 9999を指定してcanvasの手前に固定します。スピナーはCSSアニメーション(@keyframesで回転)だけで作れます。JavaScriptやSVGのアニメーションを使わなくても、ブラウザがレンダリングを始めた直後から動き出すため、最初の「何も起きていない」時間をほぼゼロにできます。

ゲームの初期化処理が終わったタイミングでdocument.getElementById('loading-screen').style.display = 'none'とするか、フェードアウトするCSSクラスを付与します。フェードアウトはopacity: 0; transition: opacity 0.4sで十分で、0.3〜0.5秒が自然に感じられます。

プログレスバーを正直に動かす

スピナーだけでも機能しますが、読み込みに2秒以上かかる場合はプログレスバーを加えると体感時間がさらに縮まります。ここで重要なのは「正直な進捗」を表示することです。

画像ファイルをまとめて読み込む場合、Promise.allを使うより個別にonloadを待ちながらカウントするほうが進捗を出しやすくなります。

js
const assets = ['bg.png', 'player.png', 'enemy.png'];
let loaded = 0;

function updateProgress() {
  loaded++;
  const pct = Math.round((loaded / assets.length) * 100);
  document.getElementById('progress-bar').style.width = pct + '%';
  document.getElementById('loading-message').textContent =
    `読み込み中… ${pct}%`;
  if (loaded === assets.length) startGame();
}

assets.forEach(src => {
  const img = new Image();
  img.onload = updateProgress;
  img.onerror = updateProgress; // エラーでも止まらないようにする
  img.src = src;
});

onerrorでもupdateProgressを呼ぶのは、1ファイルの読み込み失敗でゲーム全体が止まらないようにするためです。フォールバック処理(代替画像の適用など)は別で行いますが、プログレスバーは止めずに進めます。

失敗しやすい点として、プログレスバーが0%のまましばらく止まるケースがあります。これは最初のonloadが来るまで何も更新されないためです。ページロード直後に「0%」を表示するだけでなく、CSSアニメーションで幅が1〜2%微妙に揺れる演出を加えると、「ちゃんと動いている」という印象を維持できます。

スマートフォンで特に注意すべき点

スマートフォンでは回線速度の差が大きく、3秒未満で終わる環境もあれば10秒以上かかる環境もあります。そのためローディング画面は「短い待ち時間でも不自然でなく、長い待ち時間でも退屈させない」設計が必要です。

短い場合:ローディング画面が一瞬だけ表示されてすぐ消えると、チラつきとして見えることがあります。最低0.5〜1秒は表示するように最小待機時間を設けるとよいです。Promise.all([loadAssets(), wait(800)])のように、リソース読み込みと最小待機を並列に走らせて両方完了したら画面を切り替えます。

長い場合:プログレスバーに加えて、テキストメッセージをフェーズごとに切り替えると読み込みに意味が生まれます。「ステージを準備しています」「敵キャラを配置しています」など、ゲームの文脈に合わせたメッセージは世界観の導入にもなります。

タップ操作:スマートフォンでは「タップしてスタート」のインタラクションを挟むと、ブラウザが音声の自動再生を許可する確率が上がります。Web Audio APIは多くのブラウザでユーザー操作後の初期化を求めるため、ローディング完了 → タップしてスタート → ゲーム開始、という流れはUX上も理にかなっています。

タッチターゲットとなるボタンは縦横44px以上を確保し、文字は16px以上にします。「TAP TO START」のような英語表記でも機能しますが、日本語プレイヤー向けには「タップしてはじめる」と添えると迷いが減ります。

世界観を伝える演出の判断基準

ローディング画面を装飾的にする場合、どこまでやるかは「読み込み処理と演出の重さのバランス」で判断します。派手なアニメーションをJavaScriptで描画すると、それ自体がCPUを消費してゲーム本体の読み込みを遅らせることがあります。

判断の目安は次の通りです。

  • CSSアニメーションのみ(スピナー、フェード)、推奨度:◎、理由:GPUで処理され、JSスレッドをほぼ使わない
  • 静止画のロゴ・タイトル表示、推奨度:◎、理由:画像1枚ならインライン埋め込みも可能
  • Lottie / SVGアニメーション、推奨度:△、理由:ファイルサイズと複雑さによる。軽量なら問題なし
  • canvasを使ったパーティクル演出、推奨度:△、理由:ゲーム本体のcanvasと分けて実装する前提で
  • 動画(mp4)の自動再生、推奨度:×、理由:モバイルでの自動再生制限・帯域負荷が大きい

ゲームの雰囲気に合ったカラーパレットをローディング画面の背景色に使うだけでも、世界観の一貫性は十分伝わります。複雑な演出より、タイトルロゴと背景色の選択を優先するほうが得られる効果に対してコストが低くなります。

次の行動

ゲームの初期化処理にすでにwindow.onloadやDOMContentLoadedを使っているなら、その直前にローディング用divを表示し、処理完了後にフェードアウトするだけで最小構成が完成します。まず1枚のスピナーと背景色だけで実装し、実際に読み込みに何秒かかるかを確認してからプログレスバーの追加を判断してください。完成したゲームはHTMLファイルまたはZIPにまとめてアソバースに投稿し、スマートフォンからも動作確認してみましょう。