Skip to content
Development
Skill

/procedural-gen

Generate game content procedurally — seeded deterministic RNG, value/Perlin/ Simplex noise for terrain and heightmaps, grid dungeon generation (rooms + corridors, BSP, random walk, wave function collapse), and weighted loot/drop tables. Engine-neutral algorithms. Use when the

From plugin
awesome-gamedev-agent-skills
6.3k77 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill procedural-gen --agent claude-code

SKILL.md

procedural-gen.SKILL.md
name: procedural-gen
description: >
  Engine-neutral procedural generation in Python with Godot and Unity mappings: seeded RNG,
  dungeon layouts, wave function collapse, noise terrain and loot tables. Use for a dungeon
  generator, WFC or Perlin terrain, or seeds that give different maps. Not for roguelike run
  structure (`roguelike`), deckbuilders (`card-game`) or painting tiles (`godot-tilemap`,
  `unity-tilemap-2d`).

Procedural generation

Generate levels, terrain and drops from rules and a seed. The property that matters most is **determinism**: one seed reproduces one world, so bugs replay and players can share seeds. This skill owns the algorithms and their validation; genre skills such as `roguelike` and `survival-crafting` decide what to generate and when.

When to use

  • Map, dungeon, cave, terrain or item-drop generators, in any engine.
  • Results that must reproduce from a seed (debugging, daily challenges, shareable worlds).
  • Validating generated content: reachable rooms, a safe spawn, a path to the exit, no key behind

its own lock.

**Not this skill:** run structure, permadeath and meta-progression (`roguelike`), deck and card pools (`card-game`), drawing tiles (`godot-tilemap`, `unity-tilemap-2d`), pathfinding over the result (`game-ai`), hand-paced levels (`level-design`).

Core workflow

1. **Own the randomness.** Create one seeded RNG and pass it to every function that makes a choice. Global random calls make results depend on call order and on other systems. 2. **Pick the technique for the content.** Continuous terrain: noise. Rooms and corridors: BSP or placement plus a spanning tree. Caves: random walk or cellular automata. Tile adjacency rules: WFC. Rarities: weighted tables. 3. **Generate into plain data** (a grid of ints, a list of rooms), separate from rendering, so it can be tested without the engine. 4. **Validate every result**: flood-fill connectivity from the spawn, spawn safety, a path to the exit, key order. Reject and regenerate with a derived seed, or repair. 5. **Tune with the seed fixed** so each parameter change is visible, then sweep many seeds to check the distribution and the failure rate. 6. **Run the seed sweep** (Pattern 4) and report its numbers.

Patterns

1. Seeded RNG

import random
rng = random.Random(seed)        # a dedicated instance, never the module-level random.*
room_count = rng.randint(5, 12)
sub = random.Random(rng.getrandbits(64))   # derive a child stream per system or per chunk

Engine equivalents: Godot 4.7 `var rng := RandomNumberGenerator.new(); rng.seed = s` (save `rng.state` to resume mid-sequence); Unity `new Unity.Mathematics.Random(seed)` (a non-zero `uint`) or `new System.Random(seed)`. Avoid `UnityEngine.Random.InitState` for generation: it seeds the one global generator every other script also draws from. Derive child seeds per system (layout, loot, decoration) so adding a decoration roll does not reshuffle the layout.

2. A PRNG whose sequence you own

class SplitMix64:
    """Tiny PRNG whose sequence you own: same seed, same numbers, on every version."""
    MASK = (1 << 64) - 1

    def __init__(self, seed: int):
        self.state = seed & self.MASK

    def next_u64(self) -> int:
        self.state = (self.state + 0x9E3779B97F4A7C15) & self.MASK
        z = self.state
        z = ((z ^ (z >> 30)) * 0xBF58476D1CE4E5B9) & self.MASK
        z = ((z ^ (z >> 27)) * 0x94D049BB133111EB) & self.MASK
        return z ^ (z >> 31)

    def below(self, n: int) -> int:            # 0 <= result < n, without modulo bias
        limit = self.MASK - (self.MASK + 1) % n
        while (x := self.next_u64()) > limit:
            pass
        return x % n

# Pin the first outputs in a test so a refactor cannot change every shared seed.
rng = SplitMix64(0)
assert [rng.next_u64() for _ in range(3)] == [
    0xE220A8397B1DCDAF, 0x6E789E6AA1B965F4, 0x06C45D188009454F]

Use it when seeds must survive engine, runtime or library updates (daily challenges, seed-only saves, cross-platform shared seeds). Port the same arithmetic to C# with `ulong` or to GDScript with care: GDScript `int` is a signed 64-bit value, so mask shifts and compare as unsigned.

3. Weighted drop table

def weighted_pick(rng, table):           # table: list of (item, weight); weights are relative
    total = sum(w for _, w in table)
    roll = rng.uniform(0, total)
    upto = 0.0
    for item, w in table:
        upto += w
        if roll < upto:
            return item
    return next(i for i, w in reversed(table) if w > 0)  # roll == total (float edge)

Built-ins do the same: Python `rng.choices(items, weights)`, Godot `rng.rand_weighted(PackedFloat32Array(weights))` (returns an index). Pure rolls produce streaks; for a guaranteed rate use a bag or pity timer (`references/dungeon-generation.md`).

4. Seed sweep (determinism, connectivity, distribution)

import hashlib, json, random, statistics

def sweep(generate, seeds=range(200), spawn=None):
    """generate(rng) -> grid (0 wall, 1 floor, solid border). Prints and returns a report."""
    bad, ratios, digests = [], [], set()
    for seed in seeds:
        grid = generate(random.Random(seed))
        if grid != generate(random.Random(seed)):
            bad.append((seed, "not deterministic"))
            continue
        floor = [(x, y) for y, row in enumerate(grid) for x, v in enumerate(row) if v]
        if not floor:
            bad.append((seed, "no floor"))
            continue
        start = spawn(grid) if spawn else floor[0]
        if len(reachable_floor(grid, start)) != len(floor):   # from dungeon-generation.md
            bad.append((seed, "unreachable floor"))
        ratios.append(len(floor) / (len(grid) * len(grid[0])))
        digests.add(hashlib.sha1(json.dumps(grid).encode()).hexdigest())
    report = {"seeds": len(seeds), "failure_count": len(bad), "failures": bad[:10],
              "distin
Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
6.3k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about procedural-gen.

Which coding agents can use procedural-gen?
procedural-gen installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.