Удаление островов
Я делал подобные вещи раньше в одной из моих игр. Чтобы избавиться от внешних островов, процесс был в основном:
- Во-первых, должна быть гарантия, что центр карты всегда будет принадлежать основной земле, и каждый пиксель начинается с «Земли» или «Воды» (т.е. разных цветов).
- Затем выполните заливку в четырех направлениях, начиная с центра карты и распространяя ее по всем клеткам «Земли». Пометить каждый пиксель, который посещает эта заливка, как другой тип, такой как «MainLand».
- Наконец, просмотрите всю карту и преобразуйте любой оставшийся пиксель «Земли» в «Вода», чтобы избавиться от других островов.
Удаление озер
Что касается избавления от дыр (или озер) внутри острова, вы делаете аналогичный процесс, но начинаете с углов карты и вместо этого растягиваетесь по плиткам «Вода». Это позволит вам отличить «Море» от других водных плиток, и тогда вы сможете избавиться от них так же, как раньше избавились от островов.
пример
Позвольте мне выкопать мою реализацию заливки, которая у меня где-то здесь (отказ от ответственности, я не заботился об эффективности, поэтому я уверен, что есть много более эффективных способов реализовать это):
private void GenerateSea()
{
// Initialize visited tiles list
visited.Clear();
// Start generating sea from the four corners
GenerateSeaRecursive(new Point(0, 0));
GenerateSeaRecursive(new Point(size.Width - 1, 0));
GenerateSeaRecursive(new Point(0, size.Height - 1));
GenerateSeaRecursive(new Point(size.Width - 1, size.Height - 1));
}
private void GenerateSeaRecursive(Point point)
{
// End recursion if point is outside bounds
if (!WithinBounds(point)) return;
// End recursion if the current spot is a land
if (tiles[point.X, point.Y].Land) return;
// End recursion if this spot has already been visited
if (visited.Contains(point)) return;
// Add point to visited points list
visited.Add(point);
// Calculate neighboring tiles coordinates
Point right = new Point(point.X + 1, point.Y);
Point left = new Point(point.X - 1, point.Y);
Point up = new Point(point.X, point.Y - 1);
Point down = new Point(point.X, point.Y + 1);
// Mark neighbouring tiles as Sea if they're not Land
if (WithinBounds(right) && tiles[right.X, right.Y].Empty)
tiles[right.X, right.Y].Sea = true;
if (WithinBounds(left) && tiles[left.X, left.Y].Empty)
tiles[left.X, left.Y].Sea = true;
if (WithinBounds(up) && tiles[up.X, up.Y].Empty)
tiles[up.X, up.Y].Sea = true;
if (WithinBounds(down) && tiles[down.X, down.Y].Empty)
tiles[down.X, down.Y].Sea = true;
// Call the function recursively for the neighboring tiles
GenerateSeaRecursive(right);
GenerateSeaRecursive(left);
GenerateSeaRecursive(up);
GenerateSeaRecursive(down);
}
Я использовал это в качестве первого шага, чтобы избавиться от озер в моей игре. После этого мне нужно было сделать что-то вроде:
private void RemoveLakes()
{
// Now that sea is generated, any empty tile should be removed
for (int j = 0; j != size.Height; j++)
for (int i = 0; i != size.Width; i++)
if (tiles[i, j].Empty) tiles[i, j].Land = true;
}
редактировать
Добавление дополнительной информации на основе комментариев. Если ваше пространство поиска слишком велико, вы можете столкнуться с переполнением стека при использовании рекурсивной версии алгоритма. Вот ссылка на stackoverflow (каламбур предназначен :-)) на нерекурсивную версию алгоритма, использующую Stack<T>
вместо этого (также в C #, чтобы соответствовать моему ответу, но должна быть легко адаптируемой к другим языкам, и есть другие реализации на этом ссылка тоже).