mcpField
P
procedural-gen

procedural-gen

github.com/gamedev-skills開発5.4k7.9k 回インストール1 日前に更新
リポジトリ

procedural-gen について

procedural-gen は、ルールとシードからレベル、地形、戦利品を生成するゲーム開発向けスキルです。再現性、接続性の検証、生成データと描画の分離を重視します。

主な機能

  1. 01シードによる決定論的生成
  2. 02ノイズによる地形・高度マップ生成
  3. 03部屋・通路・洞窟のレイアウト
  4. 04重み付き戦利品の選択
  5. 05接続性の検証

活用シーン

  1. 01再現可能なダンジョンマップの生成
  2. 02地形やバイオームの構築
  3. 03レア度に応じたアイテムドロップ

インストール

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

$npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill procedural-gen

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

同梱ファイルreferences/dungeon-generation.mdreferences/noise.md

詳細

プロシージャル生成

コンパクトなルールとシードから、レベル、地形、戦利品を生成します。優れたプロシージャル生成に欠かせないのは、決定性です。単一のシードで同じ世界を再現できれば、バグを再現でき、プレイヤー同士でシードを共有できます。このスキルでは、ノイズ、シード付き RNG、ダンジョンレイアウト、重み付きテーブルなど、中核となるアルゴリズムを扱います。roguelike や survival-crafting などのジャンルがこれを利用します。

使用する場面

  • マップ、ダンジョン、地形の高さマップ、アイテムドロップなど、手作業で作成したくないコンテンツの生成に使用します。
  • シードから再現可能な結果が必要な場合(デバッグ、デイリーチャレンジ、共有可能なワールド)に使用します。
  • 重み付きのランダムな結果(戦利品のレア度、スポーンテーブル)の選択に使用します。

使用しない場面: 生成結果を描画するためのエンジンのタイル API には、godot-tilemap または unity-tilemap-2d を使用します。生成されたマップ内で AI を経路探索させるには、game-ai を使用します。丁寧に手作業でペース配分したレベルには、level-design を使用します。プロシージャル生成と手作業によるデザインは相互補完的であり、置き換え可能なものではありません。

基本的なワークフロー

  1. 乱数を自分で管理します。 シード付き RNG インスタンスを 1 つ作成し、あらゆる場所に渡します。生成コードでグローバルまたは静的な乱数を呼び出してはいけません。結果が再現できず、呼び出し順にも依存するようになります。
  2. コンテンツに合った手法を選びます。 連続的な地形/高さマップ → ノイズ。離散的な部屋/通路 → 空間分割またはエージェントによる掘削。レア度のある結果 → 重み付きテーブル。
  3. まずプレーンなデータグリッド/配列に生成します。 描画とは切り離してください。生成処理では int[][] または辞書にデータを格納し、別の処理で描画します。
  4. プレイヤーに結果を渡す前に検証します。 すべての部屋に到達できますか? スポーン地点は安全ですか? 出口までの道はありますか? 条件を満たさないレイアウトは却下または修正し、壊れたマップをプレイヤーに渡してはいけません。
  5. シードを固定して調整します。 そうすることで各パラメーター変更の影響を個別に確認できます。その後、シードを複数試して、運のよいマップ 1 つだけでなく分布を確認します。

パターン

1. シード付きの決定的 RNG(基礎)
import random
rng = random.Random(seed)        # a dedicated instance — NOT the global random.*
room_count = rng.randint(5, 12)  # same seed -> same sequence, every run
# RIGHT: thread `rng` through every function that makes a choice.
# WRONG: calling random.randint(...) (global state) — order-dependent, unseedable.

エンジンでの同等の方法:Godot では var rng = RandomNumberGenerator.new(); rng.seed = s、Unity では var rng = new System.Random(seed)(または UnityEngine.Random.InitState)です。ワールドを再生成できるよう、セーブファイルにシードを保存します。

2. 高さマップ用のフラクタル(fBm)ノイズ
# Sum several octaves: each higher octave has higher frequency, lower amplitude.
def fbm(noise, x, y, octaves=5, lacunarity=2.0, gain=0.5):
    total, amp, freq, norm = 0.0, 1.0, 1.0, 0.0
    for _ in range(octaves):
        total += amp * noise(x * freq, y * freq)   # noise() returns ~0..1
        norm  += amp                                # track total amplitude
        amp   *= gain                               # each octave contributes less
        freq  *= lacunarity                         # ...at a higher frequency
    return total / norm                             # normalize back into 0..1

# Redistribute to carve flat valleys / sharpen peaks: higher exp -> more lowland.
elevation = pow(fbm(noise, nx, ny), 2.2)

実績のあるノイズライブラリ(FastNoiseLite、opensimplex、Unity.Mathematics.noise、または Mathf.PerlinNoise)を使い、勾配ノイズを自分で実装してはいけません。**標高と湿度には異なるシードを設定します。**そうしないと、両方のフィールドを使ったバイオーム検索の相関が完全になってしまいます。バイオーム検索の全体像と島の形状作成については、references/noise.md を参照してください。

3. 重み付き戦利品テーブル(レア度に応じた選択)
# Roll proportional to weight: common drops far more often than legendary.
def weighted_pick(rng, table):           # table: list of (item, weight)
    total = sum(w for _, w in table)
    roll = rng.uniform(0, total)          # a point on the cumulative line
    upto = 0.0
    for item, w in table:
        upto += w
        if roll < upto:                   # first bucket the roll falls into
            return item
    return table[-1][0]                   # float-safety fallback

loot = weighted_pick(rng, [("common", 70), ("rare", 25), ("legendary", 5)])

重みの合計を 100 にする必要はありません。重みは相対値です。好ましくない連続結果を防ぐには、「天井」/バッグ方式を使います(分布に関するメモは references/dungeon-generation.md を参照してください)。

4. 部屋と通路のダンジョン(概要)
# 1. Place non-overlapping rooms; 2. connect them; 3. carve into the grid.
rooms = []
for _ in range(attempts):
    r = Rect(rng.randint(1, W-w-1), rng.randint(1, H-h-1), w, h)
    if not any(r.intersects(o.expand(1)) for o in rooms):  # keep a 1-tile gap
        rooms.append(r)
for a, b in zip(rooms, rooms[1:]):       # connect each room to the next
    carve_l_corridor(grid, a.center, b.center, rng)   # horizontal then vertical

完全な生成器(BSP 分割、L 字型通路、到達可能性チェック、ランダムウォーク洞窟)は references/dungeon-generation.md にあります。

注意点

  • 生成処理でグローバル RNG を使うこと:ワールドの再現性が失われ、呼び出し順序が変わっただけでも結果が変わります。必ずシードを指定したインスタンスを渡してください。
  • 相関したノイズフィールド:標高と湿度を同じシード/オフセットからサンプリングすると、帯状に並ぶバイオームが生成されます。各フィールドのオフセットを変えるか、シードを設定し直してください。
  • オクターブによるアーティファクト:正規化せずにオクターブを追加すると、値が 0..1 の範囲を外れます。振幅の合計で割ってください(また、ライブラリの出力範囲にも注意してください。一部は -1..1 を返し、別のものは 0..1 を返します)。
  • 接続性チェックを行わないこと:部屋や洞窟が孤立することがあります。プレイ開始前にスポーン地点からフラッドフィルを行い、到達不能な領域を破棄するか、再接続してください。
  • 配置ループに上限を設けないこと:「N 個の部屋が配置できるまで試す」処理は、グリッドが小さいと永久に回り続ける可能性があります。試行回数に上限を設け、部屋の数が少なくなることを許容してください。
  • グローバルに一度だけシードを設定し、その後フレームタイミングに依存すること:時間、物理演算、ハッシュのランダム化など、非決定的な入力が生成処理に入り込むと、再現性が失われます。

参考資料

  • references/noise.md — オクターブ/ラクナリティ/ゲイン、再分布、島の形状設定、2 軸によるバイオーム検索、ブルーノイズによるオブジェクト散布。
  • references/dungeon-generation.md — BSP、部屋と通路、ランダムウォークによる洞窟生成、セル・オートマトンによる平滑化、接続性の検証、分布/救済テーブル。

関連スキル

  • godot-tilemap、unity-tilemap-2d — 生成したグリッドをエンジン上に描画します。
  • game-ai — 生成したグラフ上で経路探索を行います。
  • level-design — プロシージャル生成を補完する、ペース配分と手作業による構成。
  • roguelike、survival-crafting — このスキルと組み合わせるジャンル。

コメント