procedural-generation について
Godot 4.3+向けのプロシージャル生成ガイドです。再現可能な乱数、ノイズ地形、ダンジョン、洞窟、ルールベースのタイル生成を扱います。
主な機能
- 01シード付き乱数による結果の再現
- 02FastNoiseLiteによるノイズ地形
- 03BSPによる部屋と通路のダンジョン
- 04セル・オートマトンによる洞窟生成
- 05WFCによるタイル制約生成
活用シーン
- 01シードで共有できるレベルの生成
- 02ノイズ地形やタイルマップの作成
- 03ダンジョンや洞窟レイアウトの構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill procedural-generationインストール後、Claude Code で/procedural-generationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/bsp-dungeons.mdcellular-automata.mdnoise-generation.mdwave-function-collapse.md詳細
Godot 4.3+ でのプロシージャル生成
すべての例は、非推奨 API を使用せず Godot 4.3+ を対象としています。まず GDScript を示し、次に C# を示します。
関連スキル: TileMapLayer の使い方については 2d-essentials、3D 地形メッシュについては 3d-essentials、ベクトルと変換については math-essentials、チャンクの読み込みとパフォーマンスについては godot-optimization を参照してください。
1. シード付き乱数
再現可能な生成には、必ずシードを使用してください。これにより、シードの共有、リプレイ、決定論的なテストが可能になります。
GDScript
# RandomNumberGenerator — per-instance, seedable
var rng := RandomNumberGenerator.new()
func generate_level(level_seed: int) -> void:
rng.seed = level_seed
var width: int = rng.randi_range(20, 40)
var height: int = rng.randi_range(15, 30)
var enemy_count: int = rng.randi_range(3, 8)
var treasure_chance: float = rng.randf_range(0.05, 0.15)
# AVOID: Global randf()/randi() — one shared generator, randomized at startup
# and advanced by every other caller, so a sequence is hard to reproduce
# USE: rng.randf(), rng.randi(), rng.randf_range(), rng.randi_range()
C#
private RandomNumberGenerator _rng = new();
public void GenerateLevel(ulong levelSeed)
{
_rng.Seed = levelSeed;
int width = _rng.RandiRange(20, 40);
int height = _rng.RandiRange(15, 30);
int enemyCount = _rng.RandiRange(3, 8);
float treasureChance = _rng.RandfRange(0.05f, 0.15f);
}
ヒント: 共有可能なレベルコードを作るには、文字列からシードを生成します:
var level_seed: int = "MyLevel".hash()(C#:GD.Hash("MyLevel"))
2. ノイズベースの生成(FastNoiseLite)
FastNoiseLite は、高さマップ、バイオーム分布、2D 地形に使用します。主なパラメーター:noise_type(Perlin / Simplex / Cellular / Value)、frequency(値が低いほど特徴が大きくなります)、seed。地形の場合は、各タイル座標でノイズをサンプリングし、その値にしきい値を適用してタイルを選択します。
基本的なノイズマップの作成方法、ノイズタイプのリファレンステーブル、2D 地形と TileMapLayer の手順については、references/noise-generation.md を参照してください。
3. BSP ダンジョン生成
二分空間分割では、矩形を再帰的により小さな矩形へ分割し、各リーフの中に部屋を作り、兄弟同士を通路でつなぎます。グリッドに沿った部屋ベースのダンジョン(ローグライクのようなもの)が生成されます。
GDScript + C# による再帰的な分割、部屋の配置、通路の接続アルゴリズムの全体については、references/bsp-dungeons.md を参照してください。
4. セル・オートマトン(洞窟生成)
グリッドを約 45% の密度でランダムな壁または床で埋め、「8 つの隣接セルのうち 5 つ以上が壁なら、セルを壁にする」という処理を 4~5 回繰り返します。その結果、まっすぐな通路のない、有機的な形状の洞窟が生成されます。
TileMapLayer との統合を含む GDScript + C# の実装全体については、references/cellular-automata.md を参照してください。
5. Wave Function Collapse(WFC)
WFC は制約ソルバーです。隣接ルールを持つタイルセットを与え、エントロピーが最も低いセルを選び、有効なタイルに確定して制約を伝播させ、この処理を繰り返します。タイルルールに従った出力が生成されますが、実装は簡単ではありません。
概念の概要と簡易版の GDScript + C# 実装については、references/wave-function-collapse.md を参照してください。
6. よくある落とし穴
| 症状 | 原因 | 対処法 |
|---|---|---|
| 毎回同じレベルになる | RNG にシードを設定していない | 生成前に rng.seed を設定する |
| 同じシードでも異なるレベルになる | グローバルな randf() / randi() は起動時にランダム化され、これらを呼び出す他のコードと共有される | 専用の RandomNumberGenerator インスタンスを使用する |
| ノイズがブロック状になる | frequency が高すぎる | frequency を下げる(0.01~0.05 を試す) |
| 洞窟がすべて壁、またはすべて床になる | fill_chance が極端すぎる、または反復回数が少なすぎる | fill_chance を 0.40~0.50、反復回数を 4~6 回にする |
| BSP の部屋が重なる | 分割位置が端に近すぎる | 分割計算で min_room_size の余白を確保する |
| WFC の矛盾(有効なタイルがない) | 隣接ルールが制限されすぎている | 許可する隣接タイルを増やすか、バックトラッキングを実装する |
| 生成に時間がかかりすぎる | マップ全体を 1 フレーム内で処理している | await get_tree().process_frame を使用して複数フレームに分散するか、スレッドを使用する |
7. 実装チェックリスト
- すべての生成処理でシード指定可能な
RandomNumberGeneratorを使用し、グローバルなrandf()/randi()は使用しない - レベルを再現できるよう、シードをセーブデータとともに保存する
-
FastNoiseLiteの周波数とオクターブを、ゲームのタイル/ワールドのスケールに合わせて調整する - フリーズを防ぐため、大規模な生成処理は複数のフレームに分散するか、スレッドで実行する
- 生成されたTileMapLayerのコンテンツでは、可能な場合、ハードコードされたタイル座標ではなく地形のオートタイルを使用する
- BSPダンジョンでは、確定前にすべての部屋が接続されていることを確認する
- 洞窟の生成では、重要地点間の到達可能性を確保するためにフラッドフィルを実行する
- プレイヤーのスポーン地点が、壁の中ではなく床タイル上にあることを検証する