Advanced Cellular Automata untuk menghasilkan gua

8

Saya mencoba membuat gua di Unity. Untuk melakukan ini, saya mencoba menggunakan automata seluler. Saya menemukan yang berikut ( Rouge Basin Cellular Automata for Caves ) yang menyerupai apa yang saya coba capai.

Namun, tutorialnya tidak sepenuhnya seperti yang saya inginkan. Saya menginginkan sesuatu seperti apa yang dihasilkan oleh situs web ini ( Don Jon Caves ) dengan pengaturan "cavernous" (lihat gambar di bawah).masukkan deskripsi gambar di sini

Seperti yang Anda lihat pada gambar, semuanya terhubung. Saya telah mencoba berbagai metode dan perpustakaan, namun tidak ada yang berhasil.

Saya telah bergumul dengan masalah ini untuk sementara waktu, dan saya sangat menghargai bimbingan apa pun.

Terima kasih

satvikb
sumber

Jawaban:

4

Saya tidak yakin pendekatan yang digunakan oleh contoh yang Anda tunjukkan, tapi di sini adalah bagaimana saya mungkin akan membuat sesuatu yang serupa ...

Pertama, buat grafik jaringan tidak terarah, sesuatu seperti ini ...

Grafik jaringan tidak diarahkan

Anda akan membuatnya dari satu set node yang ditempatkan secara acak, termasuk setidaknya satu yang mewakili pintu masuk / keluar gua Anda.

Sekarang setelah Anda memiliki grafik ini, bayangkan jika Anda pertama kali membuka satu set perikop di sepanjang setiap titik - hanya perikop lurus yang sederhana, bukan tidak beraturan.

Sekarang pada dasarnya Anda punya gua, tetapi dengan dinding yang sangat halus. Akan terlihat seperti ini dari grafik di atas ...

Garis gua

Jadi yang harus dilakukan adalah mengambil dinding itu dan "mengikis" mereka untuk membuat dinding yang kasar dan tidak teratur. Mengambil contoh di sini, inilah yang mungkin Anda dapatkan ...

Gua terkikis

Dan jika dalam prosesnya, Anda terkikis ke dalam aula lain, maka tidak masalah - Anda baru saja membuat gua baru!

Gambar grafik asli berasal dari http://mathinsight.org/undirected_graph_definition

Tim Holt
sumber
Cukup mudah untuk menempatkan node secara acak, tetapi metrik macam apa yang digunakan untuk menghubungkannya? Apakah orang biasanya memilih n node? Atau mungkin mereka harus memiliki kedekatan tertentu?
Kyle Baran
Jika Anda memerlukan distribusi semi-reguler, mulailah dengan kisi yang sempurna, kemudian acak posisi node +/- agak jauh. Jika itu tidak cukup, tambahkan beberapa pengecualian acak yang menggandakan jarak acak. Anda dapat menambahkan beberapa ketebalan acak ke garis penghubung menggunakan tekstur cloud plasma untuk memilih ketebalan dengan cara yang tampaknya organik.
Stephane Hockenhull
1
Menghubungkan node adalah masalah lain yang terpisah. Inilah satu pertanyaan yang membahasnya -> mathematica.stackexchange.com/questions/11962/... Bahkan jika garis-garis saling bersilangan, metode ini masih valid.
Tim Holt
Itu benar-benar turun ke persyaratan. Jika Anda baik-baik saja dengan apa pun, Anda bisa menyelesaikannya dengan cukup sederhana. Jika Anda menginginkan pendekatan yang rumit, Anda bahkan dapat menghitung spanning tree minimum dan membuat koridor berakhir jika mereka menabrak koridor lain (saya melakukan sesuatu yang mirip dengan Ruby roguelike yang saya tulis sekali).
ashes999
Saya akan menghasilkan grafik ini sebagai Peta Jalan Probabalistik . Mulailah dengan menciptakan seperangkat "hambatan" yang dianggap tidak bisa dilewati. Ini dapat dilakukan dengan menggunakan Perlin Noise. Kemudian, tempatkan N node secara acak dan seragam di ruang kosong. Hubungkan setiap node ke node terdekat K sehingga koneksi ada di ruang kosong. Hasilnya kemungkinan akan terhubung, dan akan terlihat sangat organik.
mklingen
1

salah satu cara untuk melakukan ini adalah mengelompokkan semua gua dengan set terpisah dan kemudian menghapus semua kecuali yang terbesar

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class DisjointSet
{
    private List<int> _parent;
    private List<int> _rank;
    public DisjointSet(int count)
    {
        _parent = Enumerable.Range(0, count).ToList();
        _rank = Enumerable.Repeat(0, count).ToList();
    }
    public int Find(int i)
    {
        if (_parent[i] == i)
            return i;
        else
        {
            int result = Find(_parent[i]);
            _parent[i] = result;
            return result;
        }
    }
    public void Union(int i, int j)
    {
        int fi = Find(i);
        int fj = Find(j);
        int ri = _rank[fi];
        int rj = _rank[fj];
        if (fi == fj) return;
        if (ri < rj)
            _parent[fi] = fj;
        else if (rj < ri)
            _parent[fj] = fi;
        else
        {
            _parent[fj] = fi;
            _rank[fi]++;
        }
    }
    public Dictionary<int, List<int>> Split(List<bool> list)
    {
        var groups = new Dictionary<int, List<int>>();
        for (int i = 0; i < _parent.Count; i++)
        {
            Vector2 p = PathFinder.Instance.TilePosition(i);
            if (PathFinder.Instance.InsideEdge(p) && list[i])
            {
                int root = Find(i);
                if (!groups.ContainsKey(root))
                {
                    groups.Add(root, new List<int>());
                }
                groups[root].Add(i);
            }
        }
        return groups;
    }
}

di sinilah saya membuat daftar seluler saya dan kadang-kadang menghapus yang kecil saya kadang-kadang menggabungkan beberapa daftar dan juga menggunakan daftar ini untuk menghasilkan dan menguraikan badan air dan flora (potongan pohon, bunga, rumput) dan kabut

private List<bool> GetCellularList(int steps, float chance, int birth, int death)
{
    int count = _width * _height;
    List<bool> list = Enumerable.Repeat(false, count).ToList();
    for (int y = 0; y < _height; y++)
    {
        for (int x = 0; x < _width; x++)
        {
            Vector2 p = new Vector2(x, y);
            int index = PathFinder.Instance.TileIndex(p);
            list[index] = Utility.RandomPercent(chance);
        }
    }
    for (int i = 0; i < steps; i++)
    {
        var temp = Enumerable.Repeat(false, count).ToList();
        for (int y = 0; y < _height; y++)
        {
            for (int x = 0; x < _width; x++)
            {
                Vector2 p = new Vector2(x, y);
                int index = PathFinder.Instance.TileIndex(p);
                if (index == -1) Debug.Log(index);
                int adjacent = GetAdjacentCount(list, p);
                bool set = list[index];
                if (set)
                {
                    if (adjacent < death)
                        set = false;
                }
                else
                {
                    if (adjacent > birth)
                        set = true;
                }
                temp[index] = set;
            }
        }
        list = temp;
    }
    if ((steps > 0) && Utility.RandomBool())
        RemoveSmall(list);
    return list;
}

di sini adalah kode yang menghapus grup kecil dari daftar

private void UnionAdjacent(DisjointSet disjoint, List<bool> list, Vector2 p)
{
    for (int y = -1; y <= 1; y++)
    {
        for (int x = -1; x <= 1; x++)
        {
            if (!((x == 0) && (y == 0)))
            {
                Vector2 point = new Vector2(p.x + x, p.y + y);
                if (PathFinder.Instance.InsideEdge(point))
                {
                    int index = PathFinder.Instance.TileIndex(point);
                    if (list[index])
                    {
                        int index0 = PathFinder.Instance.TileIndex(p);
                        int root0 = disjoint.Find(index0);
                        int index1 = PathFinder.Instance.TileIndex(point);
                        int root1 = disjoint.Find(index1);
                        if (root0 != root1)
                        {
                            disjoint.Union(root0, root1);
                        }
                    }
                }
            }
        }
    }
}
private DisjointSet DisjointSetup(List<bool> list)
{
    DisjointSet disjoint = new DisjointSet(_width * _height);
    for (int y = 0; y < _height; y++)
    {
        for (int x = 0; x < _width; x++)
        {
            Vector2 p = new Vector2(x, y);
            if (PathFinder.Instance.InsideEdge(p))
            {
                int index = PathFinder.Instance.TileIndex(p);
                if (list[index])
                {
                    UnionAdjacent(disjoint, list, p);
                }
            }
        }
    }
    return disjoint;
}
private void RemoveSmallGroups(List<bool> list, Dictionary<int, List<int>> groups)
{
    int biggest = 0;
    int biggestKey = 0;
    foreach (var group in groups)
    {
        if (group.Value.Count > biggest)
        {
            biggest = group.Value.Count;
            biggestKey = group.Key;
        }
    }
    var remove = new List<int>();
    foreach (var group in groups)
    {
        if (group.Key != biggestKey)
        {
            remove.Add(group.Key);
        }
    }
    foreach (var key in remove)
    {
        FillGroup(list, groups[key]);
        groups.Remove(key);
    }
}
private void FillGroup(List<bool> list, List<int> group)
{
    foreach (int index in group)
    {
        list[index] = false;
    }
}
private void RemoveSmall(List<bool> list)
{
    DisjointSet disjoint = DisjointSetup(list);
    Dictionary<int, List<int>> groups = disjoint.Split(list);
    RemoveSmallGroups(list, groups);
}
private bool IsGroupEdge(List<bool> list, Vector2 p)
{
    bool edge = false;
    for (int y = -1; y <= 1; y++)
    {
        for (int x = -1; x <= 1; x++)
        {
            if (!((x == 0) && (y == 0)))
            {
                Vector2 point = new Vector2(p.x + x, p.y + y);
                if (PathFinder.Instance.InsideMap(point))
                {
                    int index = PathFinder.Instance.TileIndex(point);
                    if (!list[index])
                    {
                        edge = true;
                    }
                }
            }
        }
    }
    return edge;
}

atau jika Anda tidak membuang yang kecil cukup taruh barang-barang Anda di gua terbesar

private List<int> Biggest(List<bool> list)
{
    DisjointSet disjoint = DisjointSetup(list);
    Dictionary<int, List<int>> groups = disjoint.Split(list);
    RemoveSmallGroups(list, groups);
    IEnumerator<List<int>> enumerator = groups.Values.GetEnumerator();
    enumerator.MoveNext();
    List<int> group = enumerator.Current;
    return group;
}

...

public int TileIndex(int x, int y)
{
    return y * Generator.Instance.Width + x;
}
public Vector2 TilePosition(int index)
{
    float y = index / Generator.Instance.Width;
    float x = index - Generator.Instance.Width * y;
    return new Vector2(x, y);
}
Rakka Rage
sumber