都市開発シミュを作る④|4度つまずいて座標系ごと作り直した話 — 真のアイソメグリッド移行【Aurum City制作】

アプリの作り方

都市開発シミュを作る④|4度つまずいて座標系ごと作り直した話 — 真のアイソメグリッド移行【Aurum City制作】

第3回で「MVPは完成した」と思っていた。ところが実機のスクリーンショットを並べた瞬間、目を疑った。建物が見切れる。建物が建ったのにタイルが残る。街並みが重なって散らばる。1つずつバグとして直したのに、症状は形を変えて4度も再発した。4度目でようやく腹をくくった。個別のバグを追うのはやめる。設計を、最初からやり直す。真因は個別のバグではなかった。正方形グリッドという座標系そのものが、載せたいアイソメトリックの建物と、幾何学的にかみ合っていなかった。座標系を丸ごと作り直した記録を、包み隠さず開く。この連載で、いちばん学びの大きい回だ。

▶ この連載の全記事はバイブコーディング開発実例(一覧)から。うまくいった話より、この「4度の失敗」のほうが役に立つはずだ。

目次
  1. この連載で作るもの(全6回の地図)
  2. 第4回のゴール
  3. Step 1. 何が起きたか:4度つまずいた時系列
  4. Step 2. 真因:正方形グリッド × アイソメアートの非互換
  5. Step 3. 大改修の進め方:一気に書かず、段階で検証する
  6. Step 4. 2:1ダイヤモンド投影の数式
  7. Step 5. 建物を「敷地のひし形」に載せる
  8. Step 6. AIで「敷地ごと」建物を作る(プレート方式)
  9. Step 7. 段階検証:「複数隣接」で必ず確かめる
  10. まとめ:第4回の要点

この連載で作るもの(全6回の地図)

  1. ① 土台と3レイヤ設計 — sim / render / ui の分離・グリッド・Canvas描画
  2. ② AIでドット絵タイルを作る — 生成→均一化→アルファ実測→組み込み、道路の自動接続
  3. ③ 街を「動かす」— 需要・成長・経済の決定論シミュレーション(MVP完成)
  4. ④ 4度つまずいて座標系ごと作り直した話(この記事)— 真のアイソメグリッドへの全面移行
  5. ⑤「面白くない」を4軸で解消する — 目標・施設・イベント・音
  6. ⑥ 静的ビルドをWordPressへ公開する — 検証と配信、そして連載の総括

第4回のゴール

この記事を読み終えると、なぜ症状が4回も再発したのか、そして座標系を「トップダウンの正方形」から「真のアイソメトリック(2:1のひし形)」へ全面的に作り替えるとはどういうことかが分かる。まず、到達点を見てほしい。ここまで持っていくのに、4度の遠回りがあった。

真のアイソメ(ダイヤモンド)グリッドに移行した都市。48×48が一枚の大きなひし形として描かれ、建物がアイソメスプライトとして整列。CS2風のダークグラスUI。
到達点:真のアイソメ(2:1ダイヤモンド)グリッドへ全面移行したあとの街。48×48マスが1枚の大きなひし形として描かれ、AI生成のアイソメ建物が破綻なく並ぶ。UIもフローティングのダークグラス基調に刷新した。

Step 1. 何が起きたか:4度つまずいた時系列

建物は、トップダウンの地形の上にアイソメトリック(斜め45°見下ろし)のスプライトを重ねる方針だった。問題は、その見え方が実機で何度直しても整わなかったことだ。

  • 1・2度目:見切れる:スプライトの下が切れている。切り抜き(クロップ)の品質問題として、生成後の後処理を直した。
  • 3度目:建物が建ってもタイルが残る:ようやく renderer 側のバグを特定。縦長ロットのスプライトを1マス幅に圧縮していた。描画幅の計算を直した。見切れとタイル残りは、実は同じ原因の2つの症状だった。
  • 4度目:まったく整わない、散らばって見える:今度は複数の建物が隣り合った実際の街並みを見た。建物どうしが重なり、ランダムに散らばって見える。ここで気づいた。直しても直しても再発するのは、個別のバグではなく前提が間違っているからだ。

💡 いちばんの教訓同じ種類の不具合(見切れる/はみ出す/整列しない/重なる)が、形を変えて2回以上再発したら、個別バグの修正をやめて「土台の前提」を疑う。そして、1個だけを見て「直った」と言わない。3度目まで、私は建物を1棟ずつ離して置いて確認していた。単体では正しく見えた。だが実際の街は「複数が隣接する」。この検証の穴が、4度目まで真因を隠していた。

Step 2. 真因:正方形グリッド × アイソメアートの非互換

アイソメトリックの建物は、奥行きを画面の横幅で表現する。だから同じ「1マス」でも、正方形グリッド上では隣り合う建物の見かけの幅がそろわず、重なりや隙間が必然的に生じる。「見切れる」「タイルが残る」「散らばる」は、すべて1つの根本原因(正方形の升目にアイソメの絵を載せる、という座標系の不整合)が、姿を変えて現れていただけだった。

個別のパッチでは、絶対に直らない。直すべきは座標系そのものだった。都市建設ゲームの定番(SimCity / Cities: Skylines 系)と同じ、真のアイソメトリック=2:1のダイヤモンド(ひし形)グリッドへ移行する。改修は大きいが、これが唯一の正解だと判断した。

Step 3. 大改修の進め方:一気に書かず、段階で検証する

過去に3回パッチが失敗している。同じ轍を踏まないため、進め方を変えた。

  • 選択肢を書き出して決める:「①ダイヤモンドグリッドへ全面移行(定番・改修大)」vs「②正方形のまま建物をフラットに作り直し(改修小・見栄え下がる)」の2案を並べ、①に決めた。
  • 数式を先に独立検証:投影の数式とファイル別の変更点を、実装前に設計として固めた。
  • 段階的に実装し、各段でスクショ確認:一気に全部書いて最後に1回だけ見る、をやめた。地形→道路→建物→実操作、と段ごとに実機で確かめてから次へ進んだ。

Step 4. 2:1ダイヤモンド投影の数式

核心はカメラの座標変換だ。マス目の座標 (gx, gy) を、ひし形に並ぶ画面座標へ写す。1マスは横 HALF_W×2・縦 HALF_H×2 のひし形。右へ1マスは「右下」へ、下へ1マスは「左下」へずれる。これが2:1アイソメの見え方だ。

// src/render/camera.ts(アイソメ移行後)
export const HALF_W = 26;   // ひし形の横半径
export const HALF_H = 13;   // 縦半径(横の半分=2:1)

// マス目(gx,gy) → 画面px。右+1マスで(+HALF_W,+HALF_H)、下+1マスで(−HALF_W,+HALF_H)
export function worldToScreen(cam: Camera, gx: number, gy: number) {
  const dx = gx - cam.offsetX;
  const dy = gy - cam.offsetY;
  return { x: (dx - dy) * HALF_W * cam.zoom, y: (dx + dy) * HALF_H * cam.zoom };
}

// 逆変換(2×2行列の逆行列・行列式 = 2·HALF_W·HALF_H·zoom² ≠ 0 なので必ず解ける)
export function screenToWorld(cam: Camera, sx: number, sy: number) {
  const px = sx / cam.zoom, py = sy / cam.zoom;
  const dx = (px / HALF_W + py / HALF_H) / 2;
  const dy = (py / HALF_H - px / HALF_W) / 2;
  return { x: dx + cam.offsetX, y: dy + cam.offsetY };
}

🔎 座標変換を1ファイルに閉じた恩恵:第1回で「camera を独立させておくと、見せ方を変えるときここだけ直せばいい」と書いた。その伏線がここで回収される。worldToScreen / screenToWorld を書き換え、地形はひし形にクリップ、道路は手続き描画に置換した。それでもsim 層(grid.ts / rules.ts / world.ts)は1行も変えていない。街のルールは座標系を知らないから、座標系を丸ごと替えても無傷だった。層を分けた最大の見返りが、この大改修だ。

Step 5. 建物を「敷地のひし形」に載せる

建物スプライトは、正方形の端ではなくフットプリント(占有マス)のひし形の「下の頂点」を基準に置く。奥にあるものから手前へ描く深度ソートも要る。ひし形投影では、並べ替えの鍵は x + y + w + h(値が小さいほど奥)になる。

// src/render/renderer.ts の drawBuildings(抜粋)
// 奥→手前に並べ替え(ひし形投影のペインターズ・アルゴリズム)
const sorted = items.sort(
  (a, b) => (a.x + a.y + a.w + a.h) - (b.x + b.y + b.w + b.h) || a.x - b.x || a.id - b.id,
);
for (const b of sorted) {
  const c = footprintCorners(cam, b.x, b.y, b.w, b.h);
  const img = /* variant(id%3) → base → 色ブロック の順にフォールバック */;
  if (img) {
    const drawW = img.naturalWidth  * cam.zoom;   // スプライトのネイティブ幅で描く(潰さない)
    const drawH = img.naturalHeight * cam.zoom;
    // w×h ひし形の下頂点は水平中央ではなく、幅の w/(w+h) の位置に来る
    const anchorFrac = b.w / (b.w + b.h);
    ctx.drawImage(img, c.bottom.x - drawW * anchorFrac, c.bottom.y - drawH, drawW, drawH);
  }
}

3度目の失敗(縦長スプライトを1マス幅に圧縮)を、ここで正しく直している。スプライトはネイティブの画素サイズ×zoomで描き、footprint幅へ引き伸ばさない。アンカーを「水平中央」ではなく w/(w+h) の位置に置くのが、ひし形にぴったり載せる鍵だ。

Step 6. AIで「敷地ごと」建物を作る(プレート方式)

建物スプライトは、ローカルの Flux dev(Q8・ComfyUI)+アイソメ用LoRA で生成する(API課金ゼロ)。ここでも大きな方針転換があった。当初は生成物から建物だけを切り抜こうとしたが、アイソメ建物の底辺はひし形(斜め2辺)なので、水平線でどこを切っても「壁が欠ける」か「敷地が残る」。切り抜き自体が構造的に無理だと判明した。

そこで、Flux が必ず描くディオラマ風の敷地ごと採用する方式に転換した(Cities: Skylines のロット方式)。切り抜きを全廃し、敷地のひし形をそのままフットプリントに使う。細長いロットは、生成で向きを制御しようとすると正方形街区に負けるので、被写体の名詞そのものを形状のレバーにした(例:townhouse rowstrip mallwarehouse shed)。これが最強の制御だった。

💡 「AIに描かせる」より「後処理で保証する」:生成AIに「必ず◯◯を描いて」と頼むより、保証が要る要素は生成後に手続きで整えるほうが確実だ。第2回の「道路は十字1枚から派生」に続く2例目で、細長ロットの向きは実測してミラーで正規化し、敷地の下頂点が正しい位置に来るよう透明パディングで整えた。生成のガチャに品質を委ねない。

アイソメ建物スプライト(住宅・商業・工業)を隙間なく隣接配置した統制テスト。各建物が敷地のひし形にぴったり載り、重なりも散らばりもない。
「敷地ごと(プレート方式)」の建物を、隙間なく隣接させた統制テスト。4度目に崩れた重なり・散らばりが解消し、各建物が敷地のひし形にぴったり載っているのが分かる。デバッグ用フックで全種を直接配置して撮影した。
Aurum City のアイソメ都市ビュー
2:1ダイヤモンド投影のアイソメ・ビュー。建物は敷地のひし形に載り、AIで「敷地ごと」生成したプレート方式なので、複数の建物が隣接しても重なりや隙間で破綻しない。

Step 7. 段階検証:「複数隣接」で必ず確かめる

今回は、各段階で実機のスクショを撮ってから次へ進んだ。①地形だけひし形化 → 隙間・重なりゼロを確認。②道路を単独のテストマップで全形状(孤立/直線/カーブ/T字/十字)確認。③建物18種をデバッグ用フックで直接、隙間なく隣接配置して撮影(=4度目に散らばった「複数隣接」の直接検証)。④実際のUI操作で自然に育つのを確認。3度目まで欠けていた「複数を隣り合わせた状態での検証」を、今回は各段で徹底した。これで5度目の失敗を避けられた。

まとめ:第4回の要点

  • 再発する症状は前提を疑う:同種の不具合が形を変えて2回以上出たら、個別バグでなくアーキテクチャ(今回は座標系)の不整合を疑う。
  • 正方形×アイソメは非互換:真のアイソメには2:1ダイヤモンドグリッド。worldToScreen を可逆な行列に。
  • 層分離が救った:座標系を全面変更しても sim は無傷。第1回の設計が、ここで最大の見返りを出した。
  • 建物はネイティブ幅・下頂点アンカー:潰さず、w/(w+h) の位置に載せ、x+y+w+h で奥から描く。
  • 保証は後処理で:切り抜き不能→敷地ごと採用、向きは実測ミラー、被写体名詞で形状を制御。
  • 複数隣接で検証:1個だけ見て「直った」と言わない。段階ごとに実機スクショで確かめる。

次回(第5回)は、見た目が整った街に「面白さ」を入れる。実は見た目が整ったあと、自分で遊んでみて「ゲームとして面白くない」と感じた。不満は4つの軸に整理できた。①目標・達成感 ②打ち手 ③変化・驚き ④BGM。都市ランクとミッション、サービス施設と幸福度、ランダムイベントとニュース速報、そして生成AIによるBGM。既存のシミュレーションを壊さずに新しいシステムを積み重ねる設計手法(専用の乱数系列と機能フラグ)まで、まとめて開く。

AI生成コンテンツの開示:アイソメ建物スプライトは、画像生成モデル FLUX.1 [dev] + アイソメ用LoRA(Isometrica)でローカル生成し、後処理・検証して使用しています。FLUX.1 [dev] は Non-Commercial License 配布ですが、生成物(Output)の商用利用はライセンス本文 Section 2(d) で明示的に許可されており、その条項を一次確認のうえ使用しています(2026-07-02 確認・モデル本体は再配布していません)。掲載コードは実際のソースからの抜粋です。© 2026 Cooliris(本作のコード・構成・データの権利は作者に帰属します)。


👉 ▶ この連載の続き(バイブコーディング開発実例)ほかのゲーム開発の実例いろんなアプリの作り方

FX Monitor Pro の作り方 — Flet で主要10通貨ペアを常時モニターするデスクトップ為替アプリFX Monitor Pro の作り方 — Flet で主要10通貨ペアを常時モニターするデスクトップ為替アプリ前のページ

GPT-5.6 Sol/Terra/Luna が Copilot に — 従量課金で3層を使い分ける次のページGPT-5.6 Sol/Terra/Luna が Copilot に — 従量課金で3層を使い分ける

ピックアップ記事

  1. 高精度OCRデスクトップアプリの作り方 — PaddleOCR-VLとPyIns…

  2. Claude Mission Control の作り方 — Tauri+Pyth…

  3. 競艇予想AIの作り方 — LightGBMで「当たる順位」を学習させる実装ガイド…

  4. New Eden Intelligence Hub の作り方 — EVE Onl…

関連記事

  1. AIアセットでゲームマップを作る — 生成スケッチの透過切り出しからレベルセレクトまで
  2. 作ったアプリを公開する|ローカルから人に使ってもらうまでの最短ルート【入門7】

    バイブコーディング

    作ったアプリを公開する|ローカルから人に使ってもらうまでの最短ルート【入門7】

    自分のパソコンの中で動くアプリを、URL1本で世界に出す。無料の公開サ…

  3. 都市開発シミュを作る⑤|「面白くない」を目標・施設・イベント・音の4軸で解消【Aurum City制作】

    アプリの作り方

    都市開発シミュを作る⑤|「面白くない」を目標・施設・イベント・音の4軸で解消【Aurum City制…

    「面白くない」を4軸(目標・打ち手・変化・音)で解消する。都市ランクと…

  4. 正解が分かる音声を自分で作って、日本語ASRを測る(そして、その限界)

    アプリの作り方

    正解が分かる音声を自分で作って、日本語ASRを測る(そして、その限界)

    文字起こしの精度を測ろうとして、正解テキストが無いことに詰まる。音声合…

  5. エラーが出たらどうする?AIへの聞き方とデバッグの基本【入門5】

    バイブコーディング

    エラーが出たらどうする?AIへの聞き方とデバッグの基本【入門5】

    赤いエラー、真っ白な画面、反応しないボタン。ここで多くの人が止まる。エ…

  6. JavaScriptでブラウザゲームを作る①|ビルド不要(file://)で動くシーン制の土台と全体設計【Archipelago Saga制作】

注目

AIで、ここまで作れる

AIで作った2D RPGを、ブラウザでそのまま遊べます。その「作り方=最後まで完成させる進め方」も実例つきで公開中。

▶ ゲームを遊ぶやり方を読む

PR

お名前.com 独自ドメイン取得(PR)

独自ドメイン:お名前.com(本サイトで使用・PR)

  1. xAI が Grok の新画像モデル「Aurora」を展開 — 自己回帰型で既存パイプラインの何を検証すべきか

    AI・テック動向

    xAI が Grok の新画像モデル「Aurora」を展開 — 自己回帰型で既存…
  2. バイブコーディングの始め方|AIツールの選び方と最初のセットアップ【入門2】

    バイブコーディング

    バイブコーディングの始め方|AIツールの選び方と最初のセットアップ【入門2】
  3. 都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【Aurum City制作】

    アプリの作り方

    都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【…
  4. ブラウザで動く都市開発シミュを作る①|React+TypeScript+Canvasの土台と3レイヤ設計【Aurum City制作】

    アプリの作り方

    ブラウザで動く都市開発シミュを作る①|React+TypeScript+Canv…
  5. WordPress 7.0.2 の緊急修正を読む — batch API の添字ズレが RCE に化けた仕組みと、自作プラグインの点検

    AI・テック動向

    WordPress 7.0.2 の緊急修正を読む — batch API の添字…
PAGE TOP

TAG CLOUD

ドラッグで回転・クリックでそのタグの記事一覧へ