Receber por email
← Voltar para o catálogo

procedural-generation

Procedural generation patterns — Perlin/Simplex noise, BSP dungeon generation, random walk, loot tables with weighted random, wave function collapse basics, seed-based reproducibility.

20estrelas
Atualizado há 6 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add XeldarAlz/everything-claude-unity

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/procedural-generation-xeldaralz/svg)](https://www.skillteca.com.br/skills/procedural-generation-xeldaralz)
#ai

Skills relacionadas

Ver todas de DevOps e Infra →

Alerta por categoria

Receba novas skills de DevOps e Infra toda segunda

1 email curto, só com as skills novas de DevOps e Infra. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Procedural Generation Patterns

Patterns for generating content at runtime: terrain with noise, dungeons with BSP, caves with random walk, loot with weighted tables, and tile layouts with wave function collapse. All patterns support seed-based reproducibility.

Seed-Based Reproducibility

Every generation algorithm should accept a seed. Given the same seed, the output is identical. This enables shareable worlds, bug reproduction, and daily challenge modes.

Critical rule: Use System.Random (not UnityEngine.Random) for deterministic generation. UnityEngine.Random is a global singleton; any other code calling it between your generation steps will change the sequence.

public class SeededRandom
{
    private System.Random _rng;
    public int Seed { get; }

    public SeededRandom(int seed)
    {
        Seed = seed;
        _rng = new System.Random(seed);
    }

    public int Next(int min, int max) => _rng.Next(min, max);
    public float NextFloat() => (float)_rng.NextDouble();
    public float Range(float min, float max) => min + (max - min) * NextFloat();
    public bool Chance(float probability) => NextFloat() < probability;

    /// <summary>Shuffle a list in place using Fisher-Yates.</summary>
    public void Shuffle<T>(IList<T> list)
    {
        for (int i = list.Count - 1; i > 0; i--)
        {
            int j = _rng.Next(0, i + 1);
            (list[i], list[j]) = (list[j], list[i]);
        }
    }
}

For world generation, derive sub-seeds from the master seed so different systems (terrain, dungeons, loot) do not interfere:

int masterSeed = 12345;
var terrainRng = new SeededRandom(masterSeed);
var dungeonRng = new SeededRandom(masterSeed + 1);
var lootRng = new SeededRandom(masterSeed + 2);

Noise-Based Terrain Generation

Use Perlin noise to generate height maps for terrain, biome maps, moisture maps, and other continuous fields.

Basic Height Map

using UnityEngine;

public static class NoiseGenerator
{
    /// <summary>
    /// Generate a 2D noise map. Values range from 0 to 1.
    /// </summary>
    public static float[,] GenerateNoiseMap(
        int width, int height, int seed,
        float scale, int octaves, float persistence, float lacunarity,
        Vector2 offset)
    {
        var map = new float[width, height];

        // Use seed to generate random octave offsets
        var rng = new System.Random(seed);
        var octaveOffsets = new Vector2[octaves];
        for (int i = 0; i < octaves; i++)
        {
            float ox = rng.Next(-100000, 100000) + offset.x;
            float oy = rng.Next(-100000, 100000) + offset.y;
            octaveOffsets[i] = new Vector2(ox, oy);
        }

        if (scale <= 0f) scale = 0.001f;

        float maxNoise = float.MinValue;
        float minNoise = float.MaxValue;

        float halfW = width / 2f;
        float halfH = height / 2f;

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                float amplitude = 1f;
                float frequency = 1f;
                float noiseHeight = 0f;

                for (int o = 0; o < octaves; o++)
                {
                    float sampleX = (x - halfW + octaveOffsets[o].x) / scale * frequency;
                    float sampleY = (y - halfH + octaveOffsets[o].y) / scale * frequency;

                    // Mathf.PerlinNoise returns 0-1; remap to -1 to 1
                    float perlin = Mathf.PerlinNoise(sampleX, sampleY) * 2f - 1f;
                    noiseHeight += perlin * amplitude;

                    amplitude *= persistence;  // Each octave contributes less
                    frequency *= lacunarity;    // Each octave has finer detail
                }

                map[x, y] = noiseHeight;

                if (noiseHeight > maxNoise) maxNoise = noiseHeight;
                if (noiseHeight < minNoise) minNoise = noiseHeight;
            }
        }

        // Normalize to 0-1
        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++)
                map[x, y] = Mathf.InverseLerp(minNoise, maxNoise, map[x, y]);

        return map;
    }
}

Parameter guide: | Parameter | Effect | Typical Value | |-------------|--------------------------------|---------------| | scale | Zoom level (higher = smoother) | 20-100 | | octaves | Layers of detail | 4-6 | | persistence | Amplitude decay per octave | 0.4-0.6 | | lacunarity | Frequency increase per octave | 1.8-2.2 |

Applying Noise to a Tilemap

using UnityEngine;
using UnityEngine.Tilemaps;

public class TerrainGenerator : MonoBehaviour
{
    [SerializeField] private Tilemap tilemap;
    [SerializeField] private TileBase waterTile;
    [SerializeField] private TileBase sandTile;
    [SerializeField] private TileBase grassTile;
    [SerializeField] private TileBase stoneTile;
    [SerializeField] private TileBase snowTile;

    [Header("Generation Settings")]
    [SerializeField] private int width = 100;
    [SerializeField] private int height = 100;
    [SerializeField] private int seed = 42;
    [SerializeField] private float scale = 30f;
    [SerializeField] private int octaves = 4;
    [SerializeField] private float persistence = 0.5f;
    [SerializeField] private float lacunarity = 2f;

    [Header("Height Thresholds")]
    [SerializeField] private float waterLevel = 0.3f;
    [SerializeField] private float sandLevel = 0.4f;
    [SerializeField] private float grassLevel = 0.7f;
    [SerializeField] private float stoneLevel = 0.85f;

    public void Generate()
    {
        tilemap.ClearAllTiles();

        var noiseMap = NoiseGenerator.GenerateNoiseMap(
            width, height, seed, scale, octaves, persistence, lacunarity, Vector2.zero);

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                float value = noiseMap[x, y];
                TileBase tile = GetTileForHeight(value);
                tilemap.SetTile(new Vector3Int(x - width / 2, y - height / 2, 0), tile);
            }
        }
    }

    private TileBase GetTileForHeight(float height)
    {
        if (height < waterLevel) return waterTile;
        if (height < sandLevel) return sandTile;
        if (height < grassLevel) return grassTile;
        if (height < stoneLevel) return stoneTile;
        return snowTile;
    }
}

BSP Dungeon Generation

Binary Space Partition creates rectangular rooms connected by corridors. It produces clean, grid-aligned dungeons suitable for roguelikes and RPGs.

Algorithm Overview

  1. Start with a large rectangle (the entire dungeon area).
  2. Recursively split it in half (horizontally or vertically) until pieces reach minimum size.
  3. Place a room inside each leaf partition (with random padding).
  4. Connect sibling rooms with corridors.

Implementation

using System.Collections.Generic;
using UnityEngine;

public class BSPDungeon
{
    public class BSPNode
    {
        public RectInt Area;
        public BSPNode Left;
        public BSPNode Right;
        public RectInt? Room;

        public bool IsLeaf => Left == null && Right == null;
    }

    private int _minPartitionSize;
    private int _minRoomSize;
    private int _roomPadding;
    private SeededRandom _rng;

    private List<RectInt> _rooms = new();
    private HashSet<Vector2Int> _corridors = new();

    public IReadOnlyList<RectInt> Rooms => _rooms;
    public IReadOnlyCollection<Vector2Int> Corridors => _corridors;

    public BSPDungeon(int minPartitionSize = 10, int minRoomSize = 4, int roomPadding = 2)
    {
        _minPartitionSize = minPartitionSize;
        _minRoomSize = minRoomSize;
        _roomPadding = roomPadding;
    }

    public int[,] Generate(int width, int height, int seed)
    {
        _rng = new SeededRandom(seed);
        _ro
Compartilhar X LinkedIn