0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ブラウザゲームに、サーバーを書かずにランキング・セーブ・オンライン対戦を付ける(Asobiai の AIGame SDK)

0
Posted at

こんにちは、AI で作ったゲームの投稿サイト「Asobiai」の案内役、アソビです。

AI に頼めばブラウザゲームはすぐできるけど、ランキングやセーブ、オンライン対戦を付けようとすると、サーバーとログインの仕組みが必要になっちゃうよね。

Asobiai では、公開したゲームに window.AIGame っていう小さな SDK が自動で入るんだ。インストールはいらなくて、ゲームの中から関数を呼ぶだけで、ランキング・セーブ・トロフィー・みんなの集計・オンライン対戦が使えるよ。この記事では、その使い方をまとめるね。

呼ぶときは ?. を付ける

window.AIGame があるのは、Asobiai から配信されているときだけ。手元で HTML を直接開いたときにはないから、window.AIGame?.submitScore(score) みたいに ?. を付けて呼んでね。こうしておけば、手元でもエラーにならずに遊べるよ。

SDK のバージョンは window.AIGame?.version でわかるよ(この記事を書いた時点では "2.4.0")。

ランキング

ゲームオーバーのときに submitScore を呼ぶと、ゲームのページにランキングが出るよ。記録されるのは、ログインしているプレイヤーそれぞれの自己ベストなんだ。

// ゲームオーバーのとき
window.AIGame?.submitScore(score);

// タイム(小さいほど上位)を記録する場合
window.AIGame?.submitScore(seconds, { order: "asc", label: "秒" });

// ランキングを複数持つ場合
window.AIGame?.submitScore(score, { board: "hard", label: "ハード" });

// 結果を受け取る
const result = await window.AIGame?.submitScore(score);
// => { ok: true, rank: 3, best: 1200 } / { ok: false, reason: "login_required" }

ランキング(board)は1つのゲームに5つまで持てるよ。スコアは、ゲームオーバーみたいな区切りで送ってね。毎フレーム送るとか、短い時間にたくさん送ると reason: "rate_limited" で断られちゃう。ゲームを始めてから3秒たたないうちに送ったスコアも受け付けないよ("too_fast")。

ログインしていない人のスコアは、いったんブラウザに預かって、ログインしたあとにランキングへ送るよ。このときゲームには { ok: false, reason: "login_required" } が返るんだ。

セーブ・ロード

save と load で、ゲームの続きを保存できるよ。ログインして遊んでいる人はアカウント(クラウド)に保存されるから、ほかの端末でも続きから遊べるんだ。ゲストや、ほかのサイトに埋め込んだゲームでは、そのブラウザに保存されるよ。

await window.AIGame?.save("slot1", { stage: 3, coins: 120, items: ["key"] });

// ロード(無ければ null)
const data = await window.AIGame?.load("slot1");

await window.AIGame?.listSaves();   // => [{ slot: "slot1", updatedAt, cloud: true }]
await window.AIGame?.deleteSave("slot1");

保存できるのは JSON にできるデータで、1スロット256KB、1ゲーム10スロットまで。クラウドに保存するときもブラウザに必ずコピーを残すから、通信できないときも動くよ。load は、クラウドとブラウザのうち新しいほうを返すんだ。1分に120回をこえる保存は断られるから、ステージをクリアしたときやメニューを閉じたときなんかに保存してね。

ゲームを更新してセーブの形が変わって、前のデータが使えなくなるときは、新しいバージョンを上げるときに「セーブデータを新しくする」を選んでね。そのバージョンからは、AIGame.save だけじゃなくて、ゲームが自分で使っている localStorage や IndexedDB も前のバージョンと別になるよ。ゲームのコードを変える必要はないし、前のデータも消えないんだ。

トロフィー

unlockAchievement でトロフィーを渡せるよ。取ったトロフィーは、ゲームのページ(取った人の割合つき)と、その人のプロフィールに出るんだ。

window.AIGame?.unlockAchievement("first_clear");

// 一覧(取ったかどうかも)
const list = await window.AIGame?.getAchievements();
// => [{ id, name, description, icon, hidden, unlocked, rate }]

トロフィーの一覧は、ゲームのファイルの一番上に asobiai.json を置いて決めるよ。公開・更新のときに読み込まれるんだ。ゲームの編集画面でも決められて、1つのゲームに50個までだよ。

{
  "achievements": [
    { "id": "first_clear", "name": "はじめてのクリア", "description": "ステージ1をクリア", "icon": "🏆" },
    { "id": "no_damage", "name": "無傷の勇者", "description": "ダメージを受けずにクリア", "icon": "art/shield.webp" },
    { "id": "secret_room", "name": "ひみつの部屋", "description": "隠し部屋を見つけた", "icon": "🔑", "hidden": true }
  ]
}

"hidden": true にしたトロフィーは、取るまで名前と説明が「???」になるよ。ゲストが取ったトロフィーはブラウザに預かって、ログインしたときに記録されるんだ。

みんなの集計

tally で1票を入れて、getTally でみんなの合計を読めるよ。アンケートや、「今日の問題をみんなは何手で解いたか」の分布なんかに使えるんだ。

// 1票入れる(1回のプレイで、同じ名前には1票だけ)
window.AIGame?.tally("favorite-stage", "forest");

// まとめて入れる(10個まで)。日替わりの問題なら名前に日付を入れる
window.AIGame?.tally({ "d20261024.result": "solved", "d20261024.moves": 5 });

// 読む
const t = await window.AIGame?.getTally("d20261024.moves");
// => { total: 128, counts: { "4": 12, "5": 61, "6": 55 } }(Asobiai の外では null)

サーバーに残るのは合計だけで、誰が何を選んだかは残らないよ。ゲストも投票できるんだ。誰でも票を入れられるから、画面に出すときは、ゲームが決めた選択肢だけを使って、知らない値は無視してね。

オンライン対戦

joinRoom で部屋に入ると、同じ部屋の人とメッセージをやり取りできるよ。

// 空いている部屋に入る(クイックマッチ。2〜16人)
const room = await window.AIGame.joinRoom({ maxPlayers: 2 });
// 友だちと同じ部屋に入るなら合言葉を渡す: joinRoom({ maxPlayers: 2, code: "ABCD" })

room.me;        // 自分 { id, name }
room.players;   // 部屋にいる人(自分も含む)
room.isHost;    // いちばん先に入った人が true

room.on("join", (player) => { /* だれか入ってきた */ });
room.on("leave", (player) => { /* だれか抜けた */ });
room.on("message", (data, from) => { /* ほかの人から届いた */ });

room.send({ type: "move", x: 3, y: 1 });   // 自分以外の全員に送る
room.send({ type: "hello" }, player.id);    // 1人にだけ送る
room.leave();

サーバーは、同じ部屋の人にメッセージを配るだけ。ゲームの開始や勝ち負けの判定は、room.isHost が true の人(ホスト)のブラウザでやるんだ。メッセージは1つ16KB、1人あたり毎秒60通までだから、毎フレーム送るより、操作したときや1秒に数回だけ送るのがおすすめだよ。

スマホの画面ロックや回線の切り替えで通信が切れても、reconnect 秒(ふだんは60秒)のあいだは同じ席に自動で戻れるよ。ホストは room.saveState で部屋の状態をサーバーに保存できて、ホストが抜けたときは、次のホストが room.loadState で読んで続きから進められるんだ。

const room = await window.AIGame.joinRoom({
  maxPlayers: 8,
  protocol: "3",   // 通信の互換バージョン。違う人は同じ部屋に入れない
  reconnect: 90,   // 通信が切れても、この秒数は同じ席に戻れる(0〜300)
});

// ホストが進行のたびに保存する。players は人ごとの秘密(その人とホストだけが読める)
await room.saveState({
  public: { round: 3, scores },
  players: { [aliceId]: { role: "spy" }, [bobId]: { role: "detective" } },
});

// ホストが変わったとき、自分がホストなら続きを読む
room.on("host", async () => {
  if (!room.isHost) return;
  const { public: pub } = await room.loadState();
  // pub.round から続ける
});

オンライン対戦が使えるのは、Asobiai のゲームページで遊んでいるときだけ。ほかのサイトへの埋め込みや、ファイルを直接開いたときは joinRoom がエラーになるよ。サーバーはメッセージを配るだけだから、ズルを完全には防げないんだ。気軽な対戦や協力プレイ向けだね。

AI に頼むときの書き方

ゲームを AI に作ってもらうときは、使う関数の名前をそのまま伝えると確実だよ。たとえばランキングなら、こんなふうに頼むんだ。

スコアがあるゲームにしてください。ゲームオーバーのときに window.AIGame?.submitScore(score) を呼んでください(AIGame が存在しない環境でもエラーにならないよう ?. を使うこと)。タイムアタックの場合は window.AIGame?.submitScore(秒数, { order: "asc" }) としてください。

SDK の説明ページには、ランキング・集計・オンライン対戦それぞれの頼み方の文面があって、コピーして使えるよ。

SDK がほかにしていること

AIGame を呼ばなくても、SDK はいくつかのことを自動でやってるよ。矢印キーやスペースを押したときに、ページがスクロールしないように止めてる。ゲームのエラーと console の出力は、作者だけが見られるコンソールに送られるんだ。サムネイルを撮れるように、WebGL の preserveDrawingBuffer も有効にしてるよ。

iframe の大きさが読み込んだあとに決まったとき、resize イベントが届かないことがあるんだ。そのときは SDK が代わりに resize を送るよ。起動したときの大きさでキャンバスを作るゲームが、単色の画面のままになるのを防ぐためなんだ。

ゲームはゲームごとに別のドメインで動くから、localStorage はほかのゲームやサイト本体と分かれてるよ。iframe の中で動くので、ページ全体をほかのページに移すことはできなくて、リンクは新しいタブで開く必要があるんだ。外部の CDN や API はふつうに使えるよ。

おわりに

ここで紹介した機能は、どれもゲームのコードから関数を呼ぶだけで使えるよ。細かい上限や更新履歴は SDK の説明ページ にあるよ。Claude Code などの AI から Asobiai に公開する方法は、https://asobiai.games/publish/mcp にまとめてあるから、よかったら見てみてね。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?