Eliminando islas
He hecho este tipo de cosas antes en uno de mis juegos. Para deshacerse de las islas exteriores, el proceso fue básicamente:
- Primero debe haber una garantía de que el centro del mapa siempre pertenecerá a la tierra principal, y cada píxel comienza como "Tierra" o "Agua" (es decir, diferentes colores).
- Luego haga un relleno de inundación de cuatro direcciones comenzando desde el centro del mapa y extendiéndose a través de cualquier mosaico "Tierra". Marque cada píxel visitado por este relleno de inundación como un tipo diferente, como "MainLand".
- Finalmente, revisa todo el mapa y convierte cualquier píxel "Tierra" restante en "Agua" para deshacerte de otras islas.
Eliminar lagos
En cuanto a deshacerse de los agujeros (o lagos) dentro de la isla, se realiza un proceso similar pero desde las esquinas del mapa y se extiende a través de los mosaicos de "Agua". Esto le permitirá distinguir el "Mar" de las otras baldosas de agua, y luego podrá deshacerse de ellas tal como lo hizo antes con las islas.
Ejemplo
Permítanme desenterrar mi implementación del relleno de inundación que tengo aquí en algún lugar (descargo de responsabilidad, no me importaba la eficiencia, así que estoy seguro de que hay muchas formas más eficientes de implementarlo):
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);
}
Usé esto como un primer paso para deshacerme de los lagos en mi juego. Después de llamar a eso, todo lo que tenía que hacer era algo como:
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;
}
Editar
Agregar información adicional basada en los comentarios. En caso de que su espacio de búsqueda sea demasiado grande, puede experimentar un desbordamiento de pila al usar la versión recursiva del algoritmo. Aquí hay un enlace en stackoverflow (juego de palabras :-)) a una versión no recursiva del algoritmo, usando en su Stack<T>
lugar (también en C # para que coincida con mi respuesta, pero debería ser fácil de adaptar a otros idiomas, y hay otras implementaciones en eso enlace también).