Advanced Cellular Automata để tạo hang động


8

Tôi đang cố gắng tạo hang động trong Unity. Để làm điều này, tôi đang cố gắng sử dụng automata di động. Tôi đã tìm thấy những điều sau đây ( Rouge Basin Cellular Automata for Cave ) giống với những gì tôi đang cố gắng thực hiện.

Tuy nhiên, hướng dẫn không hoàn toàn là những gì tôi muốn. Tôi muốn một cái gì đó giống như những gì được tạo ra bởi trang web này ( Hang động Don Jon ) với cài đặt "hang động" (xem hình ảnh bên dưới).nhập mô tả hình ảnh ở đây

Như bạn có thể thấy trong hình ảnh, mọi thứ đều được kết nối. Tôi đã thử rất nhiều phương pháp và thư viện, tuy nhiên không có gì hiệu quả.

Tôi đã vật lộn với vấn đề này trong một thời gian, và tôi sẽ đánh giá cao bất kỳ hướng dẫn nào.

Cảm ơn

Câu trả lời:


4

Tôi không chắc cách tiếp cận được sử dụng bởi ví dụ bạn trình bày, nhưng đây là cách tôi có thể bắt đầu tạo ra thứ gì đó tương tự ...

Đầu tiên, tạo một biểu đồ mạng không mong muốn, đại loại như thế này ...

Đồ thị mạng vô hướng

Bạn sẽ tạo nó từ một tập hợp các nút được đặt ngẫu nhiên, bao gồm ít nhất một nút đại diện cho lối vào / lối ra hang động của bạn.

Bây giờ bạn đã có biểu đồ này, hãy tưởng tượng nếu trước tiên bạn mở một tập các đoạn dọc theo mỗi đỉnh - chỉ là các đoạn thẳng đơn giản, không phải là bất thường.

Bây giờ về cơ bản bạn đã có một hang động, nhưng với những bức tường rất mịn. Nó sẽ trông giống như thế này từ biểu đồ trên ...

Đường hang

Vì vậy, điều cần làm sau đó là lấy những bức tường đó và "ăn mòn" chúng để tạo ra những bức tường thô và không đều. Lấy ví dụ ở đây, đây là những gì bạn có thể nhận được ...

Hang động bị xói mòn

Và nếu trong quá trình đó, bạn ăn mòn vào một hội trường khác, thì không vấn đề gì - bạn vừa tạo một hang động mới!

Hình ảnh biểu đồ ban đầu là từ http://mathinsight.org/undirected_graph_def định


Thật dễ dàng để đặt các nút một cách ngẫu nhiên, nhưng loại số liệu nào được sử dụng để kết nối chúng? Mọi người thường chọn n nút? Hoặc có lẽ họ phải gần gũi nhất định với nhau?
Kyle Baran

Nếu bạn cần một phân phối bán thường xuyên bắt đầu với một lưới hoàn hảo, sau đó chọn ngẫu nhiên các vị trí nút +/- một số khoảng cách. Nếu không đủ, thêm một số ngoại lệ ngẫu nhiên nhân đôi khoảng cách ngẫu nhiên. Bạn có thể thêm một số độ dày ngẫu nhiên vào các đường kết nối bằng cách sử dụng kết cấu đám mây plasma để chọn độ dày theo cách có vẻ hữu cơ.
Stephane Hockenhull

1
Kết nối các nút là một vấn đề riêng biệt khác. Đây là một câu hỏi thảo luận về nó -> mathicala.stackexchange.com/questions/11962/NH Ngay cả khi các dòng giao nhau, phương thức vẫn hợp lệ.
Tim Holt

Nó thực sự đi xuống theo yêu cầu. Nếu bạn ổn với bất cứ điều gì, bạn có thể thực hiện việc này khá đơn giản. Nếu bạn muốn một cách tiếp cận phức tạp, bạn thậm chí có thể tính toán một cây bao trùm tối thiểu và có hành lang chấm dứt nếu chúng va vào một hành lang khác (tôi đã làm một cái gì đó tương tự trong Ruby roguelike tôi đã viết một lần).
tro999

Tôi sẽ tạo ra biểu đồ này dưới dạng Bản đồ Đường đi bộ . Bắt đầu bằng cách tạo ra một "chướng ngại vật" được coi là không thể vượt qua. Điều này có thể được thực hiện bằng cách sử dụng tiếng ồn Perlin. Sau đó, đặt N nút ngẫu nhiên và đồng đều trong không gian trống. Kết nối mỗi nút với K nút gần nhất của nó sao cho kết nối ở trong không gian trống. Kết quả có khả năng được kết nối, và sẽ trông rất hữu cơ.
mklingen

1

một cách để làm điều này là nhóm tất cả các hang động với một tập hợp rời rạc và sau đó loại bỏ tất cả trừ cái lớn nhất

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;
    }
}

Đây là nơi tôi tạo danh sách di động của mình và đôi khi loại bỏ những danh sách nhỏ đôi khi tôi kết hợp nhiều danh sách và cũng sử dụng những danh sách này để tạo và phác thảo các cơ quan nước và thực vật (các mảng cây, hoa, cỏ) và sương mù

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;
}

đây là mã loại bỏ các nhóm nhỏ khỏi danh sách

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;
}

hoặc nếu bạn không loại bỏ nhỏ, chỉ cần đặt công cụ của bạn vào hang lớn nhất

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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.