はじめに
この記事は 「MM2025 開発記」 シリーズ第5回です。
前回の記事ではゲームの雰囲気に合わせたCSSの設計について紹介しました。
今回は、地味だけど重要なローディング画面とアセット管理について紹介します。
なぜローディング画面が必要か
ブラウザゲームでは、以下のようなものを読み込む必要がある。
- Webフォント
- 背景画像
- キャラクター画像
- UI画像
- 音楽・歌詞情報(TextAlive)
これらは非同期で(並行して)読み込まれるため、すべて読み込まれる前にゲームが開始されてしまうと、
- フォントが適用されていない
- 画像が表示されない
- 音楽が流れない
というような問題が発生してしまう。
特に今回の作品では、音楽や歌詞情報を外部API(TextAlive)から取得するため、通信状況によって読み込みに時間がかかりやすい。よってローディング画面が無いと 「ゲームは開始できる&画像は正常、だけど音楽は流れない」 ということが起こる。
これらの問題を防ぎ、プレイヤーに快適な操作環境を提供するためには、ローディング画面の実装が欠かせない。
よって今回の作品は「全部そろってから始める」という考えを重視して設計した。
画像読み込み処理
new Image()
JavaScriptで画像を扱うときは、Image()を使う。
const coinImg = new Image();
coinImg.src = coinImgSrc;
ここで行っているのは、
- new Image() で画像オブジェクトを作る
-
.srcに画像のパスを指定する
という処理である。
しかし、この時点ではまだ画像の読み込みは完了していない。
よって、「読み込みが終わったかどうか」を確認する必要がある。
画像読み込みをPromiseで待つ
Promiseを利用し、「この画像の読み込みが終わったら準備OK!」という仕組みを作っていく。
loadImage関数
function loadImage(img) {
return new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = () => {
console.error("画像の読み込みに失敗:", img.src);
reject();
};
});
}
この関数では、
-
img.onload→ 読み込み成功 -
img.onerror→ 読み込み失敗
をPromiseに変換している。
Promise.all
async function waitForAllImages() {
try {
await Promise.all([
loadImage(groundImg),
loadImage(coinImg),
...charaImgs.map(img => loadImage(img)),
loadImage(goalFlagImg),
loadImage(cityImg),
loadImage(moImg),
loadImage(PEffectImg),
loadImage(NEffectImg),
loadImage(blockImg)
]);
Promise.allとは
- 配列の中の全ての
Promiseが終わるまで待つ - 失敗したものがあれば
catchに飛ぶ
つまり、全ての画像が読み込まれるまで次へ進まない安全装置的なもの。
Webフォントも待つ
Canvasでフォントを使用するため、フォントも読み込み待ちする。
if (document.fonts && document.fonts.load) {
await document.fonts.load("bold 60px 'Press Start 2P'");
await document.fonts.load("bold 48px 'Press Start 2P'");
await document.fonts.ready;
}
ゲーム開始
setupGame();
この処理の位置が重要。
画像・フォントどちらも準備OKになってからゲーム開始イベントを登録することで、トラブル無くゲームを開始できる。
エラー時の処理
catch (e) {
alert("画像の読み込みに失敗しました。ファイル名・パス・拡張子を再確認してください。");
}
これを書いておくことで、画像読み込みに失敗した際コンソールだけでなくアラートとして画面に表示される。
開発中のエラー確認にも便利(コンソールを開かなくてもポップアップが出るのですぐわかるから)なのでおすすめ。
画像・フォント読み込み完了後の流れ
画像・フォントのロード完了に加えて、TextAlive側の準備完了も待つため、onVideoReadyをローディング終了のトリガーとしている。
※TextAlive側の準備については、次回の記事でTextAlive App API導入として解説する。
const loadingScreen = document.getElementById("loadingScreen");
if (loadingScreen) {
setTimeout(() => {
loadingScreen.style.display = "none";
}, 2000);
}
今回の流れを整理すると、
- 画像をすべて読み込む
- フォントも読み込む
- ローディング画面を消す
- スタートボタンを有効にする
- ゲーム開始
となっている。
さいごに
今回の記事では、ローディング画面の重要性と画像の読み込み処理について解説しました。
次回は、実際にコード中でTextAlive App APIを利用し楽曲・歌詞情報を取得する方法について紹介します。