design-procedural-gen について
ジャンルを問わないゲーム向けのプロシージャル生成スキルです。アルゴリズム選定、制約、検証、キャッシュを含む、シードベースの決定的な生成手法を解説します。
主な機能
- 01シードベースの決定的な乱数生成
- 02コンテンツ別のアルゴリズム選定
- 03生成予算と再試行上限
- 04検証とキャッシュのパターン
- 05ダンジョンと戦利品の例
活用シーン
- 01再現可能なダンジョン配置の生成
- 02ノイズを使った地形の作成
- 03重み付きテーブルによる戦利品調整
- 04シードから生成内容を再現
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ fcsouza/ agent-skills --skill design-procedural-genインストール後、Claude Code で/design-procedural-genを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
boilerplate/dungeon-generator.tsloot-table.tstemplates/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):「どのプレイも違った感覚であるべきですが、どのプレイも公平に感じられるべきです。」
- すべてにシードを設定する — 同じシードなら同じ結果になります。これにより、デバッグ、共有、バグの再現、決定論的なリプレイが可能になります。
- 生成と検証を分離する — 自由に生成してから制約を検証します。無限ループの危険がある、生成とチェックを繰り返すタイトなループは決して使いません。
- ダンジョンの部屋には BSP(Binary Space Partitioning)を使う — 密度のバランスを取りやすく、制御しやすい方法です。空間を再帰的にリーフノードへ分割し、各リーフに部屋を配置します。
- 連続的な変化にはパーリンノイズ/シンプレックスノイズを使う — 地形、天候、環境の変化、資源密度などに使用します。常に一貫性のある滑らかな結果になります。
- 戦利品には重み付きランダムテーブルを使う — 純粋なランダム性は避けます。重み、救済システム、確定ドロップでばらつきを制御します(
game-economy-designも参照)。 - タイルベースのワールドには波動関数崩壊を使う — タイル間の隣接ルールを定義し、エントロピーが最も低いセルから確定させます。ルールに従った一貫性のあるレイアウトが生成されます。
- 生成前に予算制約を設ける — 部屋数、敵の密度、アイテム数、経路の長さの最小値と最大値を、生成前に設定します。予算こそがデザインです。
- 段階的に生成する — まずマクロ構造(ワールド → 地域 → エリア)を生成し、次にミクロな詳細(部屋 → 家具 → アイテム)を生成します。各レイヤーが次のレイヤーを制約します。
- キャッシュするか再生成するかを決め、両方は行わない — コンテンツの種類ごとに決定します。生成コストが高く変更頻度が低いものは DB に保存し、生成コストが低くシードで決定できるものは必要に応じて再生成します。
- 環境間で決定性を保つ — 既知の 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 年)