【JavaScript応用講座】localStorage卒業、IndexedDBでゲームデータを保存する

1. はじめに

最近、Webゲームを制作している中で、「素材の保存先」 という壁にぶつかりました。

ローカルストレージ(localStorage)は手軽ですが、容量制限は約5MB。

たとえば 画像素材をまとめたZIPファイル(3〜5MB) を保存しようとすると、すぐに限界に達してしまいます。

ゲーム開発者のリアルな悩み

  • キャラクター画像やボイス素材をプレイヤーごとに切り替えたい
  • 素材をZIPでまとめて配布したいが、毎回ダウンロードは避けたい
  • キャッシュされた素材でオフラインプレイさせたい

こうしたニーズを叶えるには、単なるlocalStorageでは太刀打ちできません。

IndexedDBとは何か?

そこで登場するのが IndexedDB。 これはブラウザ内に存在する、構造化されたローカルデータベースです。

  • 最大数百MB以上の保存容量
  • 画像(Blob)やファイルを扱える
  • データベース的にキーと値を管理可能

一見すると難しそうに見えますが、モダンなラッパーライブラリ idb を使えば驚くほど簡単に扱えます。

このあとは、localStorageとIndexedDBの違いを表で比較しつつ、 実際のゲーム素材保存&読み込み方法までステップを追って紹介していきます。

2. localStorageとの違い

localStorage はWeb開発ではおなじみの保存手段ですが、構造の単純さゆえに限界もあります。

ここでは localStorage と IndexedDB を実際のユースケースに照らして比較してみましょう。

✅ 主な比較表

特徴・仕様 localStorage IndexedDB
保存容量 約5MB(ブラウザ依存) 数十〜数百MB(圧倒的に多い)
保存形式 文字列のみ(JSONに変換して保存) 文字列、数値、オブジェクト、Blob、ArrayBuffer など
データ構造 キーと値のみ(シンプル) オブジェクトストア(テーブル)とキー、インデックス
非同期対応 ❌ 同期処理(setItem / getItem 非同期APIPromise / await)対応
速度 小規模データでは高速 複雑な検索や大量データに最適化済み
使いやすさ ◎(手軽だが限界あり) ◯(慣れれば柔軟だがAPIは複雑)
ユースケース スコア保存、テーマ設定など 画像/音声のキャッシュ、ZIPやアセットの管理

🧠 どちらを使うべきか?

  • localStorage は 設定値やフラグ、軽量スコアなどの用途に最適。
  • 一方で IndexedDB は 素材・アセット・Blob形式の大容量データを保存したいときに真価を発揮します。

🔑 キーワードは「構造化された大容量データの永続化」

🕹 ゲーム開発での具体例

使用場面 向いている保存先
ユーザーの音量設定(数値) localStorage
ゲームの進行履歴(JSON) localStorage
ZIPファイルで管理する画像セット(3MB超) IndexedDB
キャラクター別の音声ファイル(Blob) IndexedDB
サムネイル一覧・コレクションギャラリー IndexedDB(+メタ情報のインデックス)

このように、それぞれの得意分野を活かして使い分けることが重要です。

特にゲームや画像を扱うWebアプリでは、IndexedDBの導入で表現の幅が大きく広がります。

次は、実際にIndexedDBをどう使っていくか、

「ゲームでの活用シーン」を軽く触れながら、第3章へ進みましょう。

3. ゲームでの活用シーン(軽く触れる)

🎮 「素材を自由に入れ替えたい」——その夢、IndexedDBで叶います。

Webゲーム開発において、以下のような実用的かつクリエイティブなシーンで IndexedDB は活躍します。

✅ 1. ZIP素材の保存(3〜5MBの画像セット)

プレイヤーがアップロードした .zip ファイルの中に、

  • カード画像(1.webp〜10.webp)
  • キャラボイス(voice_01.ogg など)
  • メタ情報(info.json)

といったアセット一式を丸ごと保存して、次回起動時にもそのまま再利用できます。

✅ 2. メタ情報と紐づけた管理

ZIPの中にある info.json に、

{
  "title": "森の図書館",
  "author": "AI Composer",
  "thumbnail": "1.webp"
}

のようなメタ情報を付けておけば、IndexedDBに保存する際に一緒に管理できます。

これにより、ユーザーの画面に以下のようなものが実現可能になります:

  • サムネイル一覧からZIPを選択
  • キャラ名や説明を表示
  • タイトル画面で「前回使ったセット」を自動表示

✅ 3. キャッシュレスなプレイ体験の構築

一度読み込んだZIPを IndexedDB に保存しておけば、2回目以降のプレイは完全オフラインでも可能。

しかも、ファイルはBlobとして保存されるため:

  • 通信不要
  • 即時ロード
  • スマホ・PCでも高速再生

💡 補足:保存はスコアや設定だけじゃない

今までは localStorage に「スコア」「音量」「言語設定」などを保存していたかもしれません。

でも IndexedDBなら、

「ゲームそのもの」=素材や世界観も保存できます。

たとえば、キャラクターを変えたら声も絵柄も変わる

—— そんな「プレイヤーが創造するゲーム体験」が、ついに実現可能なのです。

次はいよいよ「IndexedDBの基本操作」をコード付きで見ていきます!

4. IndexedDBの基本操作

🧠 IndexedDBって難しい? たしかに素のIndexedDB APIは以下のように:

  • 非同期コールバック地獄
  • トランザクションの管理が煩雑
  • ブラウザ依存コードが増えやすい

……など、「挫折ポイント」が多く、慣れないとかなりツライです。

でも、救世主があります。

🦸‍♂️ idbライブラリ(by Jake Archibald)

Googleの開発者 Jake Archibald 氏が作った idb を使えば、たった数行でIndexedDBの操作が可能になります。

✅ 基本の流れ:データベースを開く

import { openDB } from "./js/idb.js"; // ローカル or CDNから読み込み

const db = await openDB("GameData", 1, {
  upgrade(db) {
    db.createObjectStore("zips"); // ストア名:zips
  }
});
  • 第1引数:DB名
  • 第2引数:バージョン(スキーマ変更時に番号を上げる)
  • upgrade()内で初期化処理(テーブル作成)

✅ データの保存(put)

ZIPファイルやBlobデータをそのまま保存可能:

await db.put("zips", zipBlob, "main.zip");
  • 第1引数:ストア名(“zips”)
  • 第2引数:保存するデータ(Blobや文字列など)
  • 第3引数:キー(“main.zip”)

✅ データの取得(get)

const zipBlob = await db.get("zips", "main.zip");
  • 第1引数:ストア名
  • 第2引数:キー

✅ データの削除(delete)

await db.delete("zips", "main.zip");

✅ 保存されているキー一覧を取得(getAllKeys)

const keys = await db.getAllKeys("zips");
console.log("保存されたZIP一覧", keys);

🧪 サンプル全体(まとめ)

import { openDB } from "./js/idb.js";

async function saveZipFile(zipBlob) {
  const db = await openDB("GameData", 1, {
    upgrade(db) {
      db.createObjectStore("zips");
    }
  });

  await db.put("zips", zipBlob, "main.zip");

  const saved = await db.get("zips", "main.zip");
  console.log("保存されたZIPファイル:", saved);
}

このように、awaitを使って直感的に書けるのが idb の魅力。

まるでメモリ変数を扱うかのように、ブラウザ上にファイル保存が可能になります。

5. ラッパーライブラリの紹介(学習コスト軽減)

😵 IndexedDBは「素のまま」だとキツい

もしあなたが idb や Dexie.js を知らずに IndexedDB を触り始めたら、

きっとこんなコードに出くわすでしょう:

const request = indexedDB.open("MyDatabase", 1);
request.onsuccess = function(event) {
  const db = event.target.result;
  const transaction = db.transaction(["store"], "readwrite");
  const store = transaction.objectStore("store");
  const putRequest = store.put({ data: "value" }, "key");
};

😱「え、何これ……もうやめたい」

そこで登場するのが、ラッパーライブラリです。

面倒なAPIの手続きをシンプルに隠蔽し、直感的な書き方を可能にします。

✅ 1. idb(軽量・最小・Google公式)

  • 作者:Jake Archibald(Google)

  • サイズ:約6KB(最小構成)

  • 学習コスト:★☆☆(Promiseベースで直感的)

  • 特徴:

    • openDB() 一発でDBが開ける
    • get/put/delete/getAllKeys が Promiseで使える
    • TypeScript対応
  • 使い方:CDNまたはESMで import { openDB } from ‘idb’

🏆「これを知ってるだけで、もう一段上のWeb開発者」

✅ 2. Dexie.js(高機能なORM的ラッパー)

  • 作者:David Fahlander 氏

  • サイズ:約30KB

  • 学習コスト:★★☆(構文は独特だが強力)

  • 特徴:

    • クエリ操作が可能(where() / filter() など)
    • 複数ストアやバージョン管理に強い
    • リアクティブ対応(liveQuery)
    • TypeScriptの型管理に強い
  • 向いている場面:

    • 複雑な構造のデータを複数管理したい場合
    • アプリの規模が大きく、拡張性が重要な場合

🧠 どう選べばいい?

状況 おすすめ
とにかく早く使いたい・学習コストを抑えたい idb 一択
複雑な構造・多機能なWebアプリを構築予定 Dexie.js も検討の余地あり

👤 ゲーム開発における判断基準

ユースケース ライブラリ選択
ZIPの保存と取得だけ idb で十分
コレクション・タグ・履歴など複数のストアが必要 Dexie.js が楽になるかも
VueやReactなどのフレームワークと併用 DexieliveQuery が便利な場合もある

🔚 まとめ

らを選んでも IndexedDB の面倒な生APIとは一切おさらばできます。

大切なのは「目的に合わせて道具を選ぶ」という開発者としての姿勢。

さて、次はいよいよ最終章、

「IndexedDBの導入タイミングと未来への可能性」 です。

6. まとめ

IndexedDBの導入タイミングは?初心者でも触る価値はあるのか?

🎯 IndexedDBを導入すべきタイミング

あなたがWeb開発、特にゲームやメディア系アプリを作っているなら、 以下のような要件が出た時こそ、導入のベストタイミングです。

✅ 導入すべきサイン

  • 「画像や音声をローカルに保存して、再利用したい」
  • 「ZIPファイルで素材を一括管理したい」
  • 「オフラインでゲームを続きから遊ばせたい」
  • 「ユーザーごとにカスタマイズ素材を保存したい」
  • 「プレイ履歴やコレクションをローカルに蓄積したい」

🧠 初心者でも避けずに触る価値あり

「IndexedDBは難しい」と言われることもありますが、それは昔の話です。

今や idb や Dexie.js といった優秀なラッパーライブラリによって、 誰でも手軽に使える技術に変わりました。

💡 保存するのは「スコア」だけじゃない。「世界」だ。

これからのゲームは、単なる点数や設定だけでなく、 素材そのもの・世界観・体験そのものをユーザーと共有できる時代に入っています。

たとえば——

  • キャラが変わると声も絵も違う
  • ZIPを配ることで、ゲーム全体が着せ替え可能
  • 好きなアセットを保存し、いつでも呼び出せる

そんな柔軟な世界を実現するカギが、このIndexedDBにあるのです。

🔚 最後に

「localStorage卒業」という言葉にピンと来たあなたは、 すでに “表現の幅” をもっと広げたい開発者です。

コードを書ける者だけが、次の時代の遊びを作れる。

その第一歩として、ぜひIndexedDBの導入にチャレンジしてみてください。

🎁 次回予告(予告してもいいなら)

  • ✅ IndexedDB + JSZip + UI統合で、「ユーザーが素材を自由に選んで遊べるゲームUI」を作る方法
  • ✅ IndexedDB × Indexed UI:「ローカルコレクションビューワー」開発日記

関連リンク