mcpField
D
design-procedural-gen

design-procedural-gen

github.com/fcsouza開発2443 回インストール1 週間前に更新
リポジトリ

design-procedural-gen について

ジャンルを問わないゲーム向けのプロシージャル生成スキルです。アルゴリズム選定、制約、検証、キャッシュを含む、シードベースの決定的な生成手法を解説します。

主な機能

  1. 01シードベースの決定的な乱数生成
  2. 02コンテンツ別のアルゴリズム選定
  3. 03生成予算と再試行上限
  4. 04検証とキャッシュのパターン
  5. 05ダンジョンと戦利品の例

活用シーン

  1. 01再現可能なダンジョン配置の生成
  2. 02ノイズを使った地形の作成
  3. 03重み付きテーブルによる戦利品調整
  4. 04シードから生成内容を再現

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/fcsouza/agent-skills --skill design-procedural-gen

インストール後、Claude Code で/design-procedural-genを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

boilerplate/
dungeon-generator.ts
loot-table.ts
templates/
world-seed-config.md

詳細

プロシージャルコンテンツ生成

目的

リプレイ性を高めるためのプロシージャルコンテンツ生成 — マップ、ダンジョン、戦利品、地形、NPC の行動のバリエーション、ワールドの構築などに使用します。ジャンルを問わず、ローグライク、サバイバルゲーム、放置ゲーム、オープンワールドタイトルなど、生成コンテンツが役立つあらゆるゲームに適用できます。

使用するタイミング

トリガー:プロシージャル生成、ダンジョン生成、マップ生成、戦利品テーブル、ワールドシード、ノイズ地形、BSP、波動関数崩壊、ランダム化、シード付き乱数、パーリンノイズ、シンプレックスノイズ、生成コンテンツ、ローグライク、リプレイ性

前提条件

  • game-economy-design — 戦利品のバランスと報酬曲線
  • postgres-game-schema — 生成コンテンツのキャッシュ、シードの保存

基本原則

Will Wright:「プレイヤーのために何かをするゲームを作ることには興味がありません。プレイヤーに反応するシミュレーションを作りたいのです。」— シンプルなルールから生まれる創発こそが、プロシージャル生成の核心です。

Derek Yu(Spelunky):「プロシージャル生成の魔法はランダム性ではなく、制約されたランダム性です。制約こそがデザインなのです。」

Tarn Adams(Dwarf Fortress):「まず世界をシミュレートし、そこから物語が生まれるようにしましょう。」

初代 Rogue の開発者(Michael Toy、Glenn Wichman):「どのプレイも違った感覚であるべきですが、どのプレイも公平に感じられるべきです。」

  1. すべてにシードを設定する — 同じシードなら同じ結果になります。これにより、デバッグ、共有、バグの再現、決定論的なリプレイが可能になります。
  2. 生成と検証を分離する — 自由に生成してから制約を検証します。無限ループの危険がある、生成とチェックを繰り返すタイトなループは決して使いません。
  3. ダンジョンの部屋には BSP(Binary Space Partitioning)を使う — 密度のバランスを取りやすく、制御しやすい方法です。空間を再帰的にリーフノードへ分割し、各リーフに部屋を配置します。
  4. 連続的な変化にはパーリンノイズ/シンプレックスノイズを使う — 地形、天候、環境の変化、資源密度などに使用します。常に一貫性のある滑らかな結果になります。
  5. 戦利品には重み付きランダムテーブルを使う — 純粋なランダム性は避けます。重み、救済システム、確定ドロップでばらつきを制御します(game-economy-design も参照)。
  6. タイルベースのワールドには波動関数崩壊を使う — タイル間の隣接ルールを定義し、エントロピーが最も低いセルから確定させます。ルールに従った一貫性のあるレイアウトが生成されます。
  7. 生成前に予算制約を設ける — 部屋数、敵の密度、アイテム数、経路の長さの最小値と最大値を、生成前に設定します。予算こそがデザインです。
  8. 段階的に生成する — まずマクロ構造(ワールド → 地域 → エリア)を生成し、次にミクロな詳細(部屋 → 家具 → アイテム)を生成します。各レイヤーが次のレイヤーを制約します。
  9. キャッシュするか再生成するかを決め、両方は行わない — コンテンツの種類ごとに決定します。生成コストが高く変更頻度が低いものは DB に保存し、生成コストが低くシードで決定できるものは必要に応じて再生成します。
  10. 環境間で決定性を保つ — 既知の PRNG アルゴリズム(mulberry32、xoshiro128)を使い、Math.random() は決して使いません。JS エンジンによる Math.random() の違いで、プラットフォームをまたいだシードの一貫性が損なわれます。

手順

1. シードと RNG の戦略を決める

シード付き PRNG を選びます。Math.random() はシードを設定できず、エンジンによって挙動が異なるため、決して使いません。

// mulberry32 — fast, deterministic, 32-bit state
const mulberry32 = (seed: number): (() => number) => {
  let s = seed | 0;
  return () => {
    s = (s + 0x6d2b79f5) | 0;
    let t = Math.imul(s ^ (s >>> 15), 1 | s);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
};

ワールドシードからシステムごとのシードを導出します。

const deriveSubSeed = (worldSeed: string, system: string, id: string): number => {
  const str = `${worldSeed}:${system}:${id}`;
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = (Math.imul(31, hash) + str.charCodeAt(i)) | 0;
  }
  return hash >>> 0;
};
2. 生成アルゴリズムを選ぶ
コンテンツの種類アルゴリズム使用する場面
ダンジョンの部屋/通路BSP ツリー部屋と廊下があるグリッドベースのレベル
オープン地形、高度マップパーリンノイズ/シンプレックスノイズ連続的で滑らかな地形
タイルベースのワールド波動関数崩壊隣接ルールのあるタイルセット
戦利品、ドロップ、報酬重み付きランダムテーブルアイテムの分布システム全般
洞窟システムセル・オートマトン有機的な洞窟状の形状
都市レイアウト、道路網L システム/ボロノイ図構造的でありながら有機的なレイアウト
3. 予算制約を定義する

生成を実行する前に、ハードリミットを定義します。

interface GenerationBudget {
  minRooms: number;
  maxRooms: number;
  minEnemies: number;
  maxEnemies: number;
  minItems: number;
  maxItems: number;
  maxRetries: number; // CRITICAL: prevent infinite loops
}
4. 生成、検証、再試行
const generateWithValidation = <T>(
  generator: (rng: () => number) => T,
  validator: (result: T) => boolean,
  seed: number,
  maxRetries: number,
): T | null => {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const rng = mulberry32(seed + attempt);
    const result = generator(rng);
    if (validator(result)) return result;
  }
  return null; // Budget exhausted — loosen constraints or fix generator
};
5. 生成コンテンツをキャッシュする

ダンジョンの全体レイアウトやワールドマップなど、生成コストの高いコンテンツは、シードをキーとしてキャッシュします。

// Store in DB via postgres-game-schema
const cacheKey = `${generatorVersion}:${seed}`;
// Regenerate on demand for cheap content (loot rolls, NPC names)

アクティブなセッション中の一時的なシードキャッシュについては、redis-game-patterns を相互参照してください。

コード例

BSP ダンジョン生成

BSP ベースの完全なダンジョンジェネレーターについては、boilerplate/dungeon-generator.ts を参照してください。使用方法:

import { DungeonGenerator } from './dungeon-generator';

const generator = new DungeonGenerator();
const dungeon = generator.generate('my-seed-123', {
  width: 80,
  height: 60,
  minRooms: 5,
  maxRooms: 12,
  minRoomSize: 4,
  maxRoomSize: 10,
  corridorWidth: 1,
});

// dungeon.rooms — array of placed rooms
// dungeon.corridors — connections between rooms
// dungeon.entrance — starting point
// dungeon.exits — exit points
// dungeon.grid — 2D cell array for rendering
重み付き戦利品テーブル

Vose のエイリアスメソッドを使用した重み付き戦利品テーブルについては、boilerplate/loot-table.ts を参照してください。使用方法:

import { LootTable } from './loot-table';

const table = new LootTable<string>()
  .add('Common Sword', 60)
  .add('Rare Shield', 25)
  .add('Epic Staff', 10)
  .add('Legendary Crown', 4)
  .add('Mythic Amulet', 1)
  .withGuaranteed('Health Potion');

const drops = table.roll(3, seededRng); // 3 weighted rolls + guaranteed item

相互参照

  • game-economy-design — 戦利品のバランス、報酬曲線、ドロップ率の調整
  • postgres-game-schema — DB での生成コンテンツのキャッシュ、シードの保存
  • redis-game-patterns — 一時的なシードキャッシュ、セッション単位の生成コンテンツ
  • game-backend-architecture — サーバー側での生成、権威あるダンジョンの状態

落とし穴とアンチパターン

  • 無限リトライループ — 制約が厳しすぎると、生成と検証が永久にループします。必ず maxRetries で再試行回数に上限を設け、適切に失敗を処理してください
  • 大規模運用時のシード衝突 — 32 ビットシードの値は約 40 億個です。1 日に 1 万個のシードを使う場合、誕生日のパラドックスにより約 6 万 5 千個のシードで衝突が発生します。大規模なゲームでは 64 ビットシードか文字列シードを使用してください
  • JS エンジン間の非決定性 — Math.random() の実装は V8、SpiderMonkey、JavaScriptCore で異なります。必ず明示的な PRNG を使用してください
  • タイトなループ内での生成 — 生成したコンテンツを即座に破棄する while(true) は CPU を浪費します。試行をまとめ、再試行回数に上限を設け、失敗率を記録してください
  • 生成コンテンツとシードの両方を保存する — どちらか一方を選んでください。両方を保存すると容量を無駄にし、ジェネレーターのバージョン変更時に同期の不具合が発生します
  • WFC に過剰な制約を設ける — 隣接ルールが多すぎると、崩壊が不可能になります。緩いルールから始めて、段階的に厳しくしてください
  • ジェネレーターのバージョン管理を忘れる — アルゴリズムを変更すると、古いシードから異なる結果が生成されます。ジェネレーターにバージョンを付け、シードと合わせて保存してください

デザイナーの哲学

Will Wright(SimCity、Spore):最高のプロシージャルシステムは創発的なものです。単純なルールが相互に作用し、複雑で予測不可能な結果を生み出します。ジェネレーターは、あなた自身を驚かせるものであるべきです。あらゆる出力を予測できるなら、システムは十分に複雑ではありません。出力がすべてカオスなら、制約が十分に厳しくありません。

Derek Yu(Spelunky):プロシージャル生成は、デザインの代わりにはなりません。生成される部屋、配置される敵、ドロップするアイテムのすべてに、意図が感じられるべきです。ジェネレーターは、あなたのデザイン意図を大規模に実現するツールです。そのためには、明確なルール、厳密な制約、慎重な調整が必要です。

Tarn Adams(Dwarf Fortress):深くシミュレーションしてください。最も豊かなプロシージャルコンテンツは、層を重ねたシミュレーションから生まれます。歴史が派閥を生み、派閥が対立を生み、対立がクエストを生みます。各層が次の層へとつながります。

参考資料

  • 「Procedural Content Generation in Games」— Noor Shaker、Julian Togelius、Mark J. Nelson
  • 「Spelunky」— Derek Yu(Boss Fight Books)
  • 「The Art of Game Design」— Jesse Schell、ランダム性に関する章
  • 「Wave Function Collapse」— Maxim Gumin(オリジナルのアルゴリズム)
  • GDC 講演:「Building Worlds with Noise」(2018 年)、「Procedural Generation in No Man's Sky」(2017 年)

コメント