Esiste un algoritmo per rilevare la "terraferma" su una mappa 2D?


28

Su questa mappa, la "terraferma" è tutta la terra che può essere collegata al centro della mappa nelle quattro direzioni cardinali (nord, sud, est, ovest - non in diagonale). inserisci qui la descrizione dell'immagine

Vorrei rilevare la terraferma e riempire i buchi. Ho pensato a tre cose:

  1. Cerca in tutte le celle non ad acqua (celle scure) se può essere collegato al centro della mappa usando un algoritmo di ricerca del percorso. Troppo caro! Ma questo potrebbe funzionare per le isole.

  2. La terraferma è piena di un secchio di vernice verde. Ogni buco è circondato da vernice ... e adesso? Se controllo tutti i punti d'acqua all'interno della terraferma per verificare la vicinanza, eliminerò alcune penisole e altre caratteristiche geografiche visualizzate sulla costa.

  3. Una specie di rilevamento dei bordi per capire la terraferma. Tieni tutto ciò che è dentro, riempilo se è acqua, rimuovi ciò che è fuori. Complesso?

Forse uno sviluppatore esperto di giochi mi può aiutare con questo, forse dandomi il nome di qualche algoritmo o tecnica conosciuta?


4
Mi chiedo solo se hai usato un qualche tipo di algoritmo per generare questa mappa. E se sì, cosa hai usato?
jgallant,

Vale la pena esaminare quando si lavora in ambienti basati su piastrelle è l' algoritmo Marching Squares . Con esso puoi rilevare e mantenere anche le isole più piccole, quindi ordinare per dimensione scartando le isole a cella singola o qualsiasi criterio tu possa avere.
William Mariager,

@Jon si! algoritmo a diamante quadrato per generare una mappa di altezza, quindi tutto un valore sotto è acqua, il resto, terra. Ho intenzione di utilizzare questo come forma del continente, quindi un altro passaggio per aggiungere dettagli del terreno.
Gabriel A. Zorrilla,

@Mind Marching Squares Algorithm sarà utile per dipingere il confine del continente. Grazie del bel suggerimento!
Gabriel A. Zorrilla,

Risposte:


32

Rimozione di isole

Ho già fatto questo genere di cose in uno dei miei giochi. Per sbarazzarsi delle isole esterne, il processo era sostanzialmente:

  1. Innanzitutto deve esserci una garanzia che il centro della mappa apparterrà sempre alla terra principale e ogni pixel inizia come "Terra" o "Acqua" (ovvero colori diversi).
  2. Quindi esegui un riempimento in quattro direzioni iniziando dal centro della mappa e diffondendosi su tutte le tessere "Terra". Contrassegna ogni pixel visitato da questo riempimento di tipo flood come un tipo diverso come "MainLand".
  3. Infine, passa su tutta la mappa e converti i pixel "Terra" rimanenti in "Acqua per sbarazzarti di altre isole.

Rimozione dei laghi

Per quanto riguarda la rimozione dei buchi (o dei laghi) all'interno dell'isola, fai un processo simile ma partendo dagli angoli della mappa e diffondendo invece le tessere "Acqua". Questo ti permetterà di distinguere il "Mare" dalle altre tessere d'acqua, e quindi puoi sbarazzartene proprio come ti sei sbarazzato delle isole prima.

Esempio

Consentitemi di approfondire la mia implementazione dell'inondazione che ho qui da qualche parte (dichiarazione di non responsabilità, non mi importava dell'efficienza, quindi sono sicuro che ci sono molti modi più efficienti per 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);
}

Ho usato questo come primo passo per sbarazzarmi dei laghi nel mio gioco. Dopo averlo chiamato, tutto ciò che dovevo fare era qualcosa del tipo:

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

modificare

Aggiunta di alcune informazioni aggiuntive in base ai commenti. Nel caso in cui lo spazio di ricerca sia troppo grande, è possibile che si verifichi un overflow dello stack quando si utilizza la versione ricorsiva dell'algoritmo. Ecco un link su StackOverflow (gioco di parole :-)) a una versione non ricorsiva dell'algoritmo, usando Stack<T>invece (anche in C # per abbinare la mia risposta, ma dovrebbe essere facile adattarsi ad altre lingue, e ci sono altre implementazioni su questo anche link).


11
Se non puoi garantire che la tessera centrale sarà sempre terra e parte della terraferma, usa il riempimento per inondare per contrassegnare ogni tessera terra come appartenente a una particolare isola (riempi inondazione da ogni tessera senza ID blob sulla mappa, contrassegnando le tessere riempite con lo stesso blobid se non ne ha già uno). Quindi rimuovere tutto tranne il blob con il maggior numero di tessere.
George Duckett,

In linea con quanto affermato da GeorgeDuckett, potresti provare gli algoritmi di rilevamento BLOB di Google: è un problema comune nel multi-touch con FTIR. Ricordo di aver escogitato un algoritmo più intelligente, ma non riesco a ricordare per la vita di me come ha funzionato.
Jonathan Dickinson,

Dato che ho riscontrato problemi di stack in PHP, ho implementato il riempimento dell'inondazione LIFO, che ha funzionato meravigliosamente.
Gabriel A. Zorrilla,

Non è un riempimento inondazione solo quando un algoritmo DFS viene chiamato da un algoritmo BFS? Per favore, spiega qualcuno.
jcora,

@Bane Che vuoi dire? La differenza tra DFS o BFS è solo l'ordine in cui i nodi sono visitati. Non credo che l'algoritmo di riempimento dell'inondazione specifichi l'ordine di attraversamento: non gli importa se riempie l'intera regione senza rivisitare i nodi. L'ordine dipende dall'implementazione. Vedi la parte inferiore della voce di Wikipedia per un'immagine che confronta l'ordine di attraversamento quando si utilizza una coda rispetto a una pila. L'implementazione ricorsiva può essere considerata anche come uno stack (poiché utilizza lo stack di chiamate).
David Gouveia,


5

Questa è un'operazione standard nell'elaborazione delle immagini. Si utilizza un'operazione in due fasi.

Inizia creando una copia della mappa. Da questa mappa, trasforma in pixel del mare tutti i pixel terrestri che delimitano il mare. Se lo fai una volta, eliminerà le isole 2x2 e ridurrà le isole più grandi. Se lo fai due volte, eliminerà le isole 4x4, eccetera.

Nella fase due, fai quasi il contrario: trasforma in pixel di terra tutti i pixel di mare che delimitano la terra, ma solo se erano pixel di terra nella mappa originale (ecco perché hai fatto una copia nella fase 1). Questo rigenera le isole nella loro forma originale, a meno che non siano state completamente eliminate nella fase 1.


1

Ho avuto un problema simile ma non nello sviluppo del gioco. Ho dovuto trovare pixel in un'immagine adiacenti l'uno all'altro e con lo stesso valore (regioni connesse). Ho provato a utilizzare un flood alluvione ricorsivo ma ho continuato a causare overflow dello stack (sono un programmatore alle prime armi: P). Quindi ho provato questo metodo http://en.wikipedia.org/wiki/Connected-component_labeling che in realtà era molto più efficiente, per il mio problema comunque.


Avresti dovuto provare la versione stack dell'algo flood e salvare i problemi dello stack. È risultato molto più semplice di CCL (che ho lavorato nelle ultime 2 settimane senza fortuna.)
Gabriel A. Zorrilla

Sì, ho provato entrambi, ma ho fatto funzionare prima il CCL: P e ho pensato che fosse un'idea chiara. Sono contento che tu abbia risolto il tuo problema :)
Elegant_Cow
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.