更新(2026年10月26日):「野生ポケモンの個体化」(あなたを覚えている野生ポケモン)と「エコー」は、作り直しのため現在は一時停止しています。下で紹介している「リアルリーグ」と「わざ工房」は稼働中です。

はじめまして。ブラウザだけで遊べる3Dのファンメイド・ポケモンMMO「ポケモンアリーナ」(日本語版)を開発している、有志の小さなチームです。このブログでは、ゲームの中身や開発の裏側を少しずつ書いていきます。

ポケモンアリーナ日本語版のトップ画面(ワンクリックでスタート)

▲日本語版のトップ画面。「ワンクリックでスタート」を押すと、ゲストとしてすぐに遊べます。


描画は three.js、サーバーは PHP + MySQL です。今月、「世界に記憶を持たせる」ための仕組みを3つ実装しました(うち「個体化」と「エコー」は現在停止中です)。設計と実装で気をつけた点をまとめます。

前提:結果を決めるのは常にサーバー

3つの仕組みすべてに共通するルールは「クライアントは結果を決めない」です。ブラウザは要求と表示だけを担当し、「誰が勝ったか」「逃げたか」「コインを得たか」は、必ずサーバー側に残っているバトル記録から読み直します。


また、各機能は「サーバーのエンドポイント1本+クライアントスクリプト1本」の小さなモジュールに分け、URLパラメータで個別に無効化できるようにしています。複数の言語サーバーへ同時に配布したり、1機能だけ切り戻したりするのが楽になります。

1. 野生のポケモンを「個体」にする

現状:2026年10月23日から、作り直しのため一時停止中です。この節は、実装したときの設計の記録です。

出現(スポーン)処理そのものは変えていません。変えたのは、出会った野生ポケモンを個体に結び付ける部分です。

  • 個体には #0248-117 のような番号(図鑑番号+種ごとの連番)が付く
  • 生まれたエリア、時間とともに上がるレベル、出会い・逃走・勝利の記録を持つ
  • 物語ができた個体には称号が付く

実装の流れ

  1. クライアントは、画面内に見えているスポーン枠の一覧を数秒ごとにサーバーへ送る(枠の構成が変わったときだけ再送)
  2. サーバーは各枠を「このプレイヤーにとっての」個体に結び付けて一定時間保持し、意味のある個体(そのプレイヤーに恨みがある、称号持ち)だけを返す
  3. クライアントはその個体の頭上に THREE.Sprite のプレートを出す(「REMEMBERS YOU · 2回逃走」など)

「強くなって戻ってきた」を本物にする

あなたから逃げた個体、あるいはあなたのチームを倒した個体は、あなた個人への恨みを覚えます。レベルが少し上がり(上限あり)、同じエリアで再び出会う確率が高くなります。逃走と勝利の合計が2回になると、タイプに応じた称号(いわタイプなら「山砕き」のような名前)が付きます。


ここで UI だけを演出にすると嘘になってしまうので、恨みを持つ個体と交戦したときは、サーバー側のバトルセッションの出現レベル自体を引き上げ、アリーナはその強化後の値から戦闘を組み立てます。

決着は「報告」ではなく「読み取り」

バトル画面が閉じたら、クライアントは「どうなった?」と問い合わせるだけで、サーバーが完了済みのバトル記録から「捕獲/逃走/撃破/敗北」を判定します。3回逃げられた個体をついに捕まえると、その個体の経歴(「3回の逃走の末に捕獲」)はそのまま引き継がれます。

2. エコー:偽のNPCプレイヤーを本物のプレイヤーの「残響」に置き換える

現状:2026年10月24日から、作り直しのため一時停止中です。

マップをにぎやかに見せるための偽プレイヤーを廃止し、エコーに置き換えました。各プレイヤーが最後に立っていた場所に、本人のプレイヤー名・本物のチーム・見た目・ランクを持ったエコーを残す仕組みでした。


誰でもエコーに挑戦でき、相手は持ち主の実際のチームでした。挑戦者が勝てばコインと経験値、エコーが守り切ればオフラインの持ち主にコインが入り、次にマップを開いたとき「留守中の報告」が表示されていました。


オフラインのプレイヤーに報酬が発生するので、経済面のガードを入れていました。

  • 同じエコーへの挑戦は1日1回まで
  • 挑戦者の報酬付き勝利数・持ち主の報酬付き防衛数に1日の上限
  • 勝敗はクライアントではなく、サーバーのバトル記録で検証

なお、毎週月曜日に上位9人のトレーナーがジムリーダーとチャンピオンになる「リアルリーグ」はエコーとは別の仕組みで、現在も稼働中です。3Dマップ上のジムの上に名前のプレートが表示されます。

3. わざ工房:100ポイントの予算で技を設計する

オリジナル技の要望は多いのですが、自由に作らせると「威力150・命中100・能力上昇付き」ばかりになります。そこで100ポイントの予算制にしました。

  • 威力(40〜120)はポイントを消費し、90を超えると単価が上がる
  • 命中率とPPはポイントを返してくれる(命中70%やPP5の技は安い)
  • 追加効果は1つだけ(やけど・まひ・どくの確率、与ダメージ回復、自分の能力+1、相手の能力-1 など、それぞれ価格あり)

目安として、かえんほうしゃ相当(威力90・命中100・PP15・やけど10%)は100ポイント中76ポイントです。


作った技は特別扱いせず、通常の技テーブルに1行として追加されるので、どのバトルモードからも公式の技と同じように名前で解決されます。


制約のうち2つはバトルエンジン由来で、無理にねじ曲げずに設計として見せています。追加効果が1つなのは、エンジンが1つの技につき1つの効果確率しか扱わないため。優先度を選べないのは、チーム戦が優先度を使わないためです。デメリットは命中とPPでのみ表現します。

おまけ:非表示タブでの img.decode()

Chrome はタブが非表示の間、img.decode() の完了を遅らせます。ローディング画面がアートワークの decode() を15秒のタイムアウト付きで待っていたため、掲示板などから Ctrl+クリックでバックグラウンドのタブに開いた人は、戻るとエラー画面になっていました。


対策は、タイムアウトを document.visibilityState === 'visible' の間だけ開始し、非表示中に失敗した場合はタブが表示された時点で静かに再試行することです。


const arm = () => {
  if (document.hidden) return;
  document.removeEventListener('visibilitychange', arm);
  timer = setTimeout(() => reject(Error('timeout')), 15000);
};
document.addEventListener('visibilitychange', arm);
arm();

遊んでみたい方へ

日本語版はブラウザだけで遊べます(PC・スマホ対応、ゲストでワンクリック開始)。基本無料で、任意の有料アイテムがあります。
https://pokemon-arena.net/


ほかの言語のサーバー一覧:
https://tamerra.com/


※有志による非公式のファンゲームで、任天堂・株式会社ポケモン・ゲームフリークとは関係ありません。本記事はAIアシスタントの支援を受けて作成しています。