ブラウザで動く都市開発シミュを作る①|React+TypeScript+Canvasの土台と3レイヤ設計【Aurum City制作】

アプリの作り方

ブラウザで動く都市開発シミュを作る①|React+TypeScript+Canvasの土台と3レイヤ設計【Aurum City制作】

「AIに任せたら、動くものは出てきた。でも、このコード、この先どう育てればいいんだ?」。バイブコーディングの最初の壁はここにある。土台の設計を自分で握っていないと、機能を1つ足すたびに全体が崩れていく。この連載では、ブラウザで動く都市開発シミュ「Aurum City」を、実際のコードで・順を追って・自分で再現できる形で1から作る。第1回のテーマは土台React + TypeScript + Canvas で、48×48マスの地図をパン・ズームしながら地形を塗れるところまでを、「あとから壊れない」設計で組み上げる。

▶ この連載の全記事はバイブコーディング開発実例(一覧)から。完成したアプリのデモは連載の最終回(第6回)で公開する。

目次
  1. この連載で作るもの(全6回の地図)
  2. 第1回のゴール
  3. Step 1. なぜ「3つの層」に分けるのか
  4. Step 2. ディレクトリ構成
  5. Step 3. sim層:グリッドとセル(画面を知らないデータ)
  6. Step 4. render層:カメラ(マス目と画面をつなぐ計算)
  7. Step 5. render層:Canvasに描く(画面外は描かない)
  8. Step 6. ui層:Reactは薄い外枠(描画ループはReactの外で回す)
  9. Step 7. 地形を塗る:Zustandで「いま選んでいる道具」を持つ
  10. まとめ:第1回の要点

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

住宅・商業・工業をゾーニングし、道路を敷き、街が自動で育ち、目標をこなし、イベントに耐える。そんなブラウザ完結の都市開発シミュを、システムごとに分けて再現していく。

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

第1回のゴール

この記事を読み終えると、48×48マスの地図がCanvasに描かれ、ドラッグでパン・ホイールでズームでき、選んだ地形をクリックで塗れるところまでが手元に出来上がる。だが本題はその見た目ではない。以降の回(AIタイル・シミュレーション・アイソメ移行)で機能をどれだけ積んでも土台が崩れないように、「ロジック・描画・UI」を最初にきれいに分ける。その骨格を体に入れるのが目的だ。まず完成形の全体像を見てほしい。

図0:3つの層に分ける — 依存は必ず片方向に流す sim(ロジック) 純TypeScript 地図・世界の状態だけ render(描画) Canvas 2D simを読んで毎フレーム描く ui(React) ツール選択・統計表示 地図そのものは描かない 状態を持つ 状態を読むだけ コマンドを送る 操作 → uiが sim へコマンド(place / bulldoze …)→ sim が状態更新 → render が次フレームで反映 データは「ui → sim → render」の一方向に流れる。逆流させないのが崩れない秘訣。

Step 1. なぜ「3つの層」に分けるのか

まず結論から。この都市シミュは、フォルダを役割で 3つ に分ける。sim(ロジック)render(描画)ui(React) だ。そして最重要のルールがひとつある。sim は描画も画面も一切知らない。sim が扱うのは「地図のマス目に何があるか」という状態だけで、それを何色でどこに描くかは render の仕事、ボタンやパネルは ui の仕事、ときっぱり分ける。

面倒に見えるかもしれない。だが、この分け方には後で効いてくる理由が3つある。

  • ロジックを高速にテストできる:sim は画面に依存しないただの関数とデータなので、ブラウザを立ち上げなくても単体テストが一瞬で回る。「需要が上がったら建物が育つ」といった核心のルールを、目視でなく自動テストで守れる(第3回で実演する)。
  • 見た目を丸ごと差し替えても、中身は無傷:描画を Canvas から別方式に変えても、sim は1行も変わらない。実際この連載の第4回では、地図の見せ方をトップダウンからアイソメトリックへ全面的に作り替えるが、sim には手を触れずに済む。層を分けておいた最大の恩恵がここで出る。
  • 「ロジック」と「見た目」を分けて説明できる:どこが街のルールで、どこが表示の都合なのかが混ざらない。自分が後から読み返すときにも、AIに手伝ってもらうときにも、迷子になりにくい。

💡 バイブコーディングの勘所:AIにコードを書かせるほど、この「層の境界」を人間が握っておく価値は上がる。境界さえ守られていれば、各層は小さく独立しているので、AIに1ファイルずつ安全に任せられる。逆に境界が無いと、1つの修正が思わぬ場所を壊し、原因も追えなくなる。

Step 2. ディレクトリ構成

3層の考え方を、そのままフォルダに落とす。src/ の下を3つに割るだけだ。

src/
├── sim/            # ① ロジック(純TS・Reactもcanvasも知らない)
│   └── grid.ts         # グリッド/セルのモデル
├── render/         # ② 描画(Canvas 2D)
│   ├── camera.ts       # 座標変換(画面 ↔ マス目)・パン・ズーム
│   ├── palette.ts      # 地形の色
│   └── renderer.ts     # 毎フレームCanvasに描く
├── ui/             # ③ React(外枠UIのみ)
│   ├── App.tsx         # canvasの設置・入力・描画ループ
│   ├── Toolbar.tsx     # ツール選択ボタン
│   └── store.ts        # Zustand(選択中ツール等)
└── main.tsx        # 起動(Appをマウントするだけ)

覚えるのは src/ の3分割だけでいい。sim=状態render=描画ui=操作。この後は、この順番(依存されない側の sim から、依存する側の ui へ)で1層ずつ作っていく。

Step 3. sim層:グリッドとセル(画面を知らないデータ)

最初に作るのは、街の土地そのもの。48×48 のマス目(グリッド)だ。各マス(セル)は、いまはまだ「地形」だけを持つ。plain(平地)・water(水)・mountain(山)の3種類。ここに色やピクセルの話は一切出てこないのが肝心なところだ。

// src/sim/grid.ts
export type Terrain = 'plain' | 'water' | 'mountain';

export interface Cell {
  terrain: Terrain;   // いまは地形だけ。道路や建物は後の回で足す
}

export class Grid {
  readonly width: number;
  readonly height: number;
  private cells: Cell[];

  constructor(width: number, height: number, fill: Terrain = 'plain') {
    this.width = width;
    this.height = height;
    // 幅×高さ ぶんのセルを一列に並べて持つ(1次元配列)
    this.cells = Array.from({ length: width * height }, () => ({ terrain: fill }));
  }

  inBounds(x: number, y: number): boolean {
    return x >= 0 && y >= 0 && x < this.width && y < this.height;
  }

  private index(x: number, y: number): number {
    if (!this.inBounds(x, y)) throw new RangeError(`out of bounds: (${x},${y})`);
    return y * this.width + x;   // (x,y) → 一列に並べた配列の位置
  }

  get(x: number, y: number): Cell { return this.cells[this.index(x, y)]; }
  set(x: number, y: number, cell: Cell): void { this.cells[this.index(x, y)] = cell; }
}

ポイントは3つ。(1) 2次元の地図を、あえて1次元の配列で持つ。(x, y)y * width + x という式で配列の位置に変換する(この式は「行を縦に積む」という意味で、後の保存・読込でもそのまま効いてくる)。(2) inBounds で範囲外を弾き、盤の外を触ろうとしたら例外で止める。(3) この Gridget / set しか外に見せない。中の配列がどう並んでいるかは、外側からは気にしなくていい。

💡 ここが分離の本体grid.tsimport 文が1つも無いことに注目してほしい。React も Canvas も、色も座標も出てこない。だからこのファイルは、ブラウザ抜きでそのままテストできる。「状態を持つ層は、見た目を知らない」。この徹底が、第4回の大改修を無傷で乗り切る布石になる。

Step 4. render層:カメラ(マス目と画面をつなぐ計算)

次は、この 48×48 のマス目を画面に映す係だ。ここで必要になるのが2つの座標系の橋渡し。マス目の座標(何マス目か)画面のピクセル座標(画面の何px目か)を、行き来できるようにする。それを担うのが camera.ts

1マスの大きさを TILE = 32(px)と決める。カメラは「いま地図のどこを・どの拡大率で見ているか」を offsetX / offsetY / zoom の3つで持つ。トップダウン(真上から見下ろす正方形マップ)なので、変換はとても素直な掛け算になる。

// src/render/camera.ts
export const TILE = 32;

export interface Camera {
  offsetX: number;   // 画面の原点に対応する「ワールドpx」
  offsetY: number;
  zoom: number;
}

// ワールドpx → 画面px(見ている位置を引いて、拡大率を掛けるだけ)
export function worldToScreen(cam: Camera, wx: number, wy: number) {
  return { x: (wx - cam.offsetX) * cam.zoom, y: (wy - cam.offsetY) * cam.zoom };
}

// 画面px → ワールドpx(上の逆算)
export function screenToWorld(cam: Camera, sx: number, sy: number) {
  return { x: sx / cam.zoom + cam.offsetX, y: sy / cam.zoom + cam.offsetY };
}

// 画面px → 「何マス目か」(ワールドpxを TILE で割って切り捨て)
export function screenToCell(cam: Camera, sx: number, sy: number) {
  const w = screenToWorld(cam, sx, sy);
  return { cx: Math.floor(w.x / TILE), cy: Math.floor(w.y / TILE) };
}

worldToScreen は「見ている左上(offset)を引いて、拡大率(zoom)を掛ける」だけ。screenToCell はその逆算で、クリックした画面の場所が地図の何マス目かを割り出す。マウス操作を地図の座標に翻訳するのは、すべてこの関数の仕事だ。

パンとズームも、この座標変換の上に乗る。特にズームは「カーソルの下にあるマスを固定したまま拡大する」ようにしたい。素朴に拡大率だけ変えると地図が左上に逃げていくので、拡大の前後でカーソル位置のワールド座標を測り、そのズレを offset で打ち消す。

export function panCamera(cam: Camera, dxScreen: number, dyScreen: number): Camera {
  // ドラッグした画面px を、拡大率で割って offset へ反映(掴んだ地図が指について来る)
  return { ...cam, offsetX: cam.offsetX - dxScreen / cam.zoom, offsetY: cam.offsetY - dyScreen / cam.zoom };
}

export function zoomCamera(cam: Camera, factor: number, sx: number, sy: number): Camera {
  const before = screenToWorld(cam, sx, sy);                 // 拡大前、カーソルの下にある点
  const zoom = Math.max(0.25, Math.min(4, cam.zoom * factor)); // 0.25〜4倍にクランプ
  const next: Camera = { ...cam, zoom };
  const after = screenToWorld(next, sx, sy);                  // 拡大後、同じ画面位置の点
  // ズレ(before − after)を offset で打ち消す=カーソル下のマスが動かない
  return { ...next, offsetX: next.offsetX + (before.x - after.x), offsetY: next.offsetY + (before.y - after.y) };
}

🔎 なぜ camera を独立させるのか:座標変換を camera.ts の数個の関数に閉じ込めておくと、「地図の見せ方」を変えるときここだけ直せばよくなる。第4回でトップダウンをアイソメトリック(斜め見下ろし)に作り替えるとき、書き換えるのは主にこのファイルの数式だけだ。いま素直な掛け算にしているのは、その伏線でもある。

Step 5. render層:Canvasに描く(画面外は描かない)

座標変換ができたので、あとは毎フレーム、全マスをCanvasに塗るだけ。ただし 48×48 = 2304 マスを何も考えず全部描くのは無駄が多い。画面の外にはみ出したマスは描かない(カリング)。このひと工夫で、拡大して一部しか見えていないときの描画がぐっと軽くなる。

// src/render/renderer.ts
import { Grid } from '../sim/grid';
import { Camera, worldToScreen, TILE } from './camera';
import { TERRAIN_COLORS } from './palette';

export class Renderer {
  constructor(private ctx: CanvasRenderingContext2D, private grid: Grid, private cam: Camera) {
    this.ctx.imageSmoothingEnabled = false;   // ドット絵をくっきり(ぼかさない)
  }

  drawFrame(hover?: { cx: number; cy: number }): void {
    const { ctx, grid, cam } = this;
    const cw = ctx.canvas.width, ch = ctx.canvas.height;
    ctx.clearRect(0, 0, cw, ch);
    const size = TILE * cam.zoom;
    for (let y = 0; y < grid.height; y++) {
      for (let x = 0; x < grid.width; x++) {
        const s = worldToScreen(cam, x * TILE, y * TILE);
        // 画面の外なら描かずに飛ばす(カリング)
        if (s.x + size <= 0 || s.y + size <= 0 || s.x >= cw || s.y >= ch) continue;
        ctx.fillStyle = TERRAIN_COLORS[grid.get(x, y).terrain];   // simの状態を「読むだけ」
        ctx.fillRect(s.x, s.y, size, size);
      }
    }
    // ホバー中のマスを、ブランドのゴールドで縁取る
    if (hover && grid.inBounds(hover.cx, hover.cy)) {
      const s = worldToScreen(cam, hover.cx * TILE, hover.cy * TILE);
      ctx.strokeStyle = '#C9A227';   // Aurum = 金。ブランドカラー
      ctx.lineWidth = 2;
      ctx.strokeRect(s.x, s.y, size, size);
    }
  }
}

地形の色は、別ファイル palette.ts に切り出しておく。「どの地形を何色で塗るか」は表示の都合であって、街のルールではない。だから sim ではなく render 側に置く。

// src/render/palette.ts
import { Terrain } from '../sim/grid';

export const TERRAIN_COLORS: Record<Terrain, string> = {
  plain: '#3f5d3a',      // 平地(草地)
  water: '#2a4b6e',      // 水
  mountain: '#6b5d4f',   // 山(建設不可になる予定)
};

ここで renderer.tsgrid.get(x, y).terrain読むだけで、書き換えていないことに注目してほしい。render は sim の状態を映す鏡であって、状態そのものは触らない。ホバー中のマスに引く #C9A227 のゴールド枠が、このアプリの「Aurum(金)」というブランドの最初の顔になる。

Canvasに描かれた48×48の平地グリッド。左上にゴールドの『Aurum City』ロゴ。
レンダラが全マスを描いた最初の状態。まだ全マスが平地(plain)なので、一面の緑になる。ここまでで sim・render・描画ループがつながった。

Step 6. ui層:Reactは薄い外枠(描画ループはReactの外で回す)

いよいよReactの出番。だが役割は意外なほど小さい。Reactは <canvas> を1枚置き、その中の描画ループはReactの再レンダーの外で回す。地図の1マスが変わるたびにReactを再描画していたら、到底60fpsは出ない。だから地図はCanvasに任せ、Reactはツールバーなどの外枠だけを受け持つ。

これを実現するのが useEffectrequestAnimationFrame の組み合わせだ。App.tsx の中核を見てみよう。

// src/ui/App.tsx(中核・抜粋)
useEffect(() => {
  const canvas = canvasRef.current; if (!canvas) return;
  const ctx = canvas.getContext('2d'); if (!ctx) return;

  const grid = new Grid(48, 48);          // sim層
  const cam  = makeCamera();              // render層(カメラ)
  const renderer = new Renderer(ctx, grid, cam);

  let hover; let dragging = false; let lastX = 0, lastY = 0;

  // 画面のマウス位置 → 何マス目か
  const cellAt = (e) => {
    const rect = canvas.getBoundingClientRect();
    return screenToCell(cam, e.clientX - rect.left, e.clientY - rect.top);
  };
  // いま選んでいる地形を、そのマスに塗る(=simを書き換える)
  const paintAt = (e) => {
    const tool = useAppStore.getState().tool;
    if (tool === 'pan') return;
    const { cx, cy } = cellAt(e);
    if (grid.inBounds(cx, cy)) grid.set(cx, cy, { terrain: tool });
  };

  const onMove = (e) => {
    hover = cellAt(e);                    // ホバー枠の位置を更新
    if (!dragging) return;
    if (useAppStore.getState().tool === 'pan') {
      const dx = e.clientX - lastX, dy = e.clientY - lastY;
      lastX = e.clientX; lastY = e.clientY;
      Object.assign(cam, panCamera(cam, dx, dy));   // ドラッグで地図を動かす
    } else paintAt(e);                    // ドラッグで連続して塗る
  };
  const onWheel = (e) => {
    e.preventDefault();
    const rect = canvas.getBoundingClientRect();
    const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
    Object.assign(cam, zoomCamera(cam, factor, e.clientX - rect.left, e.clientY - rect.top));
  };
  // …down/up/leave のリスナ登録は省略…

  // 描画ループ:毎フレーム、いまの状態を描き直すだけ
  let raf = 0;
  const loop = () => { renderer.drawFrame(hover); raf = requestAnimationFrame(loop); };
  raf = requestAnimationFrame(loop);

  // 後始末:アンマウント時にループとリスナを必ず止める
  return () => { cancelAnimationFrame(raf); /* removeEventListener … */ };
}, []);

構造はシンプルだ。useEffect の中で sim・render を1回だけ組み立て、requestAnimationFrame のループが毎フレーム drawFrame を呼ぶ。マウス操作は、パンならカメラを動かし、地形ツールなら grid.set で sim を書き換える。「入力で状態を変える → ループが次のフレームで映す」という、図0のデータフローがそのまま形になっている。

💡 つまずきポイントuseEffect の最後で return する後始末を書き忘れると、開発中のホットリロードのたびに requestAnimationFrame のループが二重・三重に走り、じわじわ重くなる。「ループやリスナを張ったら、必ず後始末で止める」。Reactでcanvasを扱うときの、いちばん大事なお作法だ。

Step 7. 地形を塗る:Zustandで「いま選んでいる道具」を持つ

最後のピース。「いまユーザーがどの道具(パン/平地/水/山)を選んでいるか」を、UI全体で共有したい。ここで軽量な状態管理ライブラリ Zustand を使う。持つのは選択中のツール1つだけ、という最小構成から始める。

// src/ui/store.ts
import { create } from 'zustand';
import { Terrain } from '../sim/grid';

export type Tool = 'pan' | Terrain;   // 'pan' | 'plain' | 'water' | 'mountain'

export interface AppState {
  tool: Tool;
  setTool: (t: Tool) => void;
}

export const useAppStore = create<AppState>((set) => ({
  tool: 'pan',
  setTool: (t) => set({ tool: t }),
}));

ここで Tool の型が 'pan' | Terrain になっているのが小さな工夫だ。地形の種類(Terrain)をそのままツールとして再利用しているので、ツールバーで「水」を選んで塗れば、その値がそのまま grid.set(cx, cy, { terrain: 'water' }) に渡る。ツールバー(Toolbar.tsx)は各ボタンで setTool を呼ぶだけの薄い部品で、選択中のツールはゴールドで強調する。

これで第1回のパーツが全部そろった。ツールバーで「水」を選び、地図をドラッグすれば青いマスが引かれ、ホバーしたマスにはゴールドの枠が付く。ホイールでカーソルの下を中心に拡大でき、パンで地図を動かせる。あなたはいま、sim(状態)・render(描画)・ui(操作)が一方向につながった、崩れない土台を動かしたことになる。

ツールバーで『水』を選び、平地の上に水タイルを斜めに塗った画面。選択中の水ボタンはゴールド、ホバー中のマスにゴールドの枠。
「水」ツールで塗った様子。選択中のツール(水)はゴールドで強調、ホバー中のマスには #C9A227 のゴールド枠が付く。地形を塗り替えるたび、grid.set が sim の状態を書き換えている。

まとめ:第1回の要点

  • 3レイヤ分離sim(ロジック・純TS)/render(Canvas描画)/ui(React外枠)。依存は「ui → sim → render」の一方向。
  • simは見た目を知らないgrid.tsimport ゼロ。だからブラウザ抜きで高速にテストでき、描画方式を変えても無傷。
  • camera=座標変換の一点集中:マス目↔画面pxの橋渡し・パン・カーソル固定ズームをここに閉じる。第4回の改修に効く。
  • 描画ループはReactの外useEffectrequestAnimationFrame でcanvasを回し、Reactは外枠だけ。後始末を必ず書く。
  • 状態管理は最小から:Zustandで「選択中ツール」だけを持ち、必要になったら足す。

次回(第2回)は、この地図にAIで作ったドット絵タイルを貼っていく。画像生成AIで平地・水・山のタイルを作り、寸法を均一化し、切り抜きを見た目でなくアルファ値の実測で検証してから組み込む。その量産フローを実コードで開き、道路を1本ずつ置くだけで交差点やカーブが自動でつながる「オートタイリング」の仕組みも作る。色のブロックだった街が、一気に「絵」になる回だ。

この連載について:Aurum City は、設計・実装・アセット生成の各工程でAIを併用して開発した個人プロジェクトです。掲載するコードは実際のソースからの抜粋で、動作は自動テストと実機で検証しています。© 2026 Cooliris(記事本文と掲載コードの権利は作者に帰属します)。


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

ブラウザゲームを自動テスト&デプロイ⑥|puppeteerヘッドレス検証と静的配信【Archipelago Saga制作・完結】ブラウザゲームを自動テスト&デプロイ⑥|puppeteerヘッドレス検証と静的配信【Archipelago Saga制作・完結】前のページ

Claude Sonnet 5 が Claude Code の既定モデルに — 1Mコンテキストと月コスト激減を実額で検証次のページClaude Sonnet 5 が Claude Code の既定モデルに — 1Mコンテキストと月コスト激減を実額で検証

ピックアップ記事

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

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

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

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

関連記事

  1. 壊れないようにアプリを育てる|機能追加・役割分け・動作確認の習慣【入門6】

    バイブコーディング

    壊れないようにアプリを育てる|機能追加・役割分け・動作確認の習慣【入門6】

    機能を足すたび別の場所が壊れる“もぐら叩き地獄”を抜ける3つの習慣。1…

  2. 都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【Aurum City制作】

    アプリの作り方

    都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【Aurum City…

    需要・成長・経済で街を動かす決定論シミュレーションの作り方。種から回す…

  3. 背景除去アプリを作る④|BiRefNetをONNXでローカル推論する — モデル別前処理とDirectML→CPUフォールバック【背景除去Studio制作】
  4. large-v3-turbo は large-v3 の代わりになるか。日本語で測って、速度の測り方を2回間違えた記録
  5. 背景除去アプリを作る⑤|5スライダー後処理+消去/復元ブラシ+20段Undoで「自動9割・手で1割」を仕上げる【背景除去Studio制作】

    アプリの作り方

    背景除去アプリを作る⑤|5スライダー後処理+消去/復元ブラシ+20段Undoで「自動9割・手で1割」…

    完全自動を狙うより、自動+微修正の方が実用品質に早く届く。しきい値・ソ…

  6. WorkSpace Explorer の作り方 — 増えた自作アプリを一画面で束ねる Electron 製の開発司令塔

    アプリの作り方

    WorkSpace Explorer の作り方 — 増えた自作アプリを一画面で束ねる Electro…

    作業フォルダのプロジェクトが増えて「どれが何で最後にいつ触ったか」を見…

注目

AIで、ここまで作れる

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

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

PR

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

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

  1. VS Code 1.128 のマルチチャット — 1セッションで2方針を並走する

    AI・テック動向

    VS Code 1.128 のマルチチャット — 1セッションで2方針を並走する…
  2. 都市開発シミュを作る⑥|静的ビルドをWordPressへ公開+連載総括【Aurum City制作・完結】

    アプリの作り方

    都市開発シミュを作る⑥|静的ビルドをWordPressへ公開+連載総括【Auru…
  3. Claude Fable 5 が7/1にグローバル再開 — Fable/Mythos/Opus/Sonnet/Haiku を用途で選ぶ完全整理

    AI・テック動向

    Claude Fable 5 が7/1にグローバル再開 — Fable/Myth…
  4. AIアセットでゲームマップを作る — 生成スケッチの透過切り出しからレベルセレクトまで

    アプリの作り方

    AIアセットでゲームマップを作る — 生成スケッチの透過切り出しからレベルセレク…
  5. Claude Cowork がデバイス横断 — 端末を閉じても続くリモート実行

    AI・テック動向

    Claude Cowork がデバイス横断 — 端末を閉じても続くリモート実行
PAGE TOP

TAG CLOUD

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