Один из способов сделать это состоит в том, чтобы сгруппировать все пещеры с непересекающимся набором, а затем удалить все, кроме самого большого
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;
}
}
Здесь я создаю свой сотовый список, а иногда удаляю маленькие, иногда объединяю несколько списков, а также использую эти списки для создания и выделения водоемов и флоры (пятна деревьев, цветов, травы) и тумана.
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;
}
вот код, который удаляет небольшие группы из списка
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;
}
или если вы не удалите маленький, просто положите свои вещи в большую пещеру
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);
}