Trovare il percorso più breve su una griglia esagonale


14

Sto scrivendo un gioco a turni che ha alcuni elementi di simulazione. Un'attività a cui sono bloccato attualmente è la ricerca del percorso. Quello che voglio fare è spostare ogni turno di un avventuriero AI di una tessera più vicino al suo bersaglio usando i suoi attuali x, y e il suo bersaglio x, y.

Nel tentativo di capirlo da solo, posso determinare 4 direzioni senza problemi usando

dx = currentX - targetY
dy = currentY - targetY

ma non sono sicuro di come determinare quale delle 6 direzioni è in realtà il percorso "migliore" o "più breve".

Ad esempio, nel modo in cui è configurato attualmente, utilizzo East, West, NE, NW, SE, SW ma per arrivare al riquadro NE mi sposto verso Est e poi verso nord-ovest invece di spostarmi solo a nord-ovest.

Spero che non sia stato tutto sconclusionato. Anche solo un link o due per iniziare sarebbe bello. La maggior parte delle informazioni che ho trovato riguarda il disegno delle griglie e il groking dello strano sistema di coordinate necessario.


5
Un * ti dà il percorso più breve indipendentemente dalla forma del tuo grafico (griglia, esadecimale, forma libera ...)
Jari Komppa

Risposte:


21

Alcune risposte!

Il sistema di coordinate che ho visto più spesso per l'attraversamento basato su esagoni è quello in cui il giocatore può muoversi in tutte le normali direzioni NSEW, così come NW e SE. Quindi esegui il rendering di ogni riga di un offset di mezzo quadrato. Ad esempio, la posizione (2,7) è considerata adiacente a (1,7), (3,7), (2,6), (2,8) e quelle strane: (1,6) e (3,8). Nel frattempo, se ipotizziamo che (2,7) sia visualizzato al centro dello schermo, (2,6) verrà visualizzato verso l'alto e verso destra, (2,8) verrà visualizzato verso il basso e verso -la sinistra, (1,7) e (3,7) la legheranno rispettivamente a sinistra e a destra, e (1,6) e (3,8) si posizioneranno rispettivamente in alto a sinistra e in basso a destra.

Un diagramma di ciò che intendo:

inserisci qui la descrizione dell'immagine

Se lo stai facendo in questo modo, trovare il percorso diretto più breve non è difficile: percorri la massima distanza NW / SE che puoi senza superare il bersaglio lungo un asse cardinale, quindi viaggia lungo tale asse verso il bersaglio.

Ma ovviamente questo ti condurrà felicemente attraverso montagne o altri terreni impraticabili. Per rispondere a una domanda che non hai ancora posto: l' algoritmo di ricerca A * è un approccio comune e ragionevolmente buono alla ricerca di percorsi. Gestirà non solo strani layout senza griglia, ma affronterà felicemente ostacoli e persino terreno ostruito / lento.


Grazie per il link all'algoritmo di ricerca A *. L'unico modo in cui posso immaginare di poter attraversare nsew e nw / se è un esagono inclinato. Che mi sembra strano nella mia testa. Puoi collegarmi ad un esempio di questo?
Timothy Mayes,

4
Sto dicendo che la tua immagine renderizzata non deve somigliare molto alla struttura interna. Sto suggerendo che internamente usi NSEW e NW / SE, ma lo mostri all'utente come se fosse una griglia. Allegare un diagramma esplicativo alla risposta originale :)
ZorbaTHut

2
Rappresentazione interessante per una griglia esadecimale. Di solito faccio uno schema frastagliato, quindi l'adiacenza è diversa per le righe pari e dispari. Ciò introduce un'ulteriore minima complessità nella ricerca del percorso, ma utilizza un array bidimensionale in modo più efficiente (supponendo che l'intera area di gioco sia un rettangolo
Panda Pajama

2
@PandaPajama: jagged funziona meglio per l'archiviazione efficiente di mappe rettangolari; puoi far funzionare bene le coordinate non frastagliate con questo trucco
entro il

2
@PandaPajama, c'è un altro trucco interessante che puoi usare: puoi usare la rappresentazione non frastagliata per le coordinate, quindi astrarre il supporto per l'archiviazione dei dati dietro qualcosa che utilizza il metodo "frastagliato". Ho scoperto che il sistema di coordinate non frastagliato è molto più facile da gestire, ma ovviamente una volta sottratto, il backend può fare tutto ciò che gli piace per rendere le cose efficienti :)
ZorbaTHut

5

Ho appena pubblicato una libreria di utilities a griglia esadecimale su CodePlex.com qui: https://hexgridutilities.codeplex.com/ La libreria include il percorso di ricerca (utilizzando A- * a la Eric Lippert) e include utility per la conversione automatica tra coordinate frastagliate (definite come utenti) e coordinate non frastagliate (definite canoniche). L'algoritmo di ricerca del percorso consente al costo del passaggio per ciascun nodo di variare sia con l'esagono di entrata che con il lato esagonale attraversato (sebbene l'esempio fornito sia più semplice). Inoltre, viene fornito un campo visivo elevato usando il casting ombra, [modifica: parole rimosse].

Ecco un esempio di codice che converte facilmente tra tre sistemi di coordinate a griglia esadecimale:

static readonly IntMatrix2D MatrixUserToCanon = new IntMatrix2D(2,1, 0,2, 0,0, 2);
IntVector2D VectorCanon {
  get { return !isCanonNull ? vectorCanon : VectorUser * MatrixUserToCanon / 2; }
  set { vectorCanon = value;  isUserNull = isCustomNull = true; }
} IntVector2D vectorCanon;
bool isCanonNull;

static readonly IntMatrix2D MatrixCanonToUser  = new IntMatrix2D(2,-1, 0,2, 0,1, 2);    
IntVector2D VectorUser {
  get { return !isUserNull  ? vectorUser 
             : !isCanonNull ? VectorCanon  * MatrixCanonToUser / 2
                            : VectorCustom * MatrixCustomToUser / 2; }
  set { vectorUser  = value;  isCustomNull = isCanonNull = true; }
} IntVector2D vectorUser;
bool isUserNull;

static IntMatrix2D MatrixCustomToUser = new IntMatrix2D(2,0, 0,-2, 0,(2*Height)-1, 2);
static IntMatrix2D MatrixUserToCustom = new IntMatrix2D(2,0, 0,-2, 0,(2*Height)-1, 2);
IntVector2D VectorCustom {
  get { return !isCustomNull ? vectorCustom : VectorUser * MatrixUserToCustom / 2; }
  set { vectorCustom  = value;  isCanonNull = isUserNull = true; }
} IntVector2D vectorCustom;
bool isCustomNull;

IntMatrix2D e IntVector2D sono implementazioni intere [modifica: omogenee] di affine2D Graphics Vector e Matrix. La divisione finale per 2 sulle applicazioni vettoriali è di ri-normalizzare i vettori; questo potrebbe essere sepolto nell'implementazione di IntMatrix2D, ma quindi la ragione del settimo argomento per i costruttori IntMatrix2D è meno ovvia. Si noti la memorizzazione nella cache combinata e la valutazione lazy delle formulazioni non correnti.

Queste matrici sono per il caso:

  • Grano esagonale verticale;
  • Origine in alto a sinistra per coordinate canoniche e utente, in basso a sinistra per coordinate personalizzate;
  • Asse Y verticalmente verso il basso;
  • Asse X rettangolare in senso orizzontale; e
  • Asse X canonico verso nord-est (cioè verso l'alto e verso destra, a 120 gradi in senso antiorario dall'asse Y).

La libreria di codici sopra menzionata fornisce un meccanismo altrettanto elegante per il picking esadecimale (ovvero identificare l'esagono selezionato con un clic del mouse).

Nelle coordinate canoniche, i 6 vettori di direzione cardinali sono (1,0), (0,1), (1,1) e le loro inversioni per tutti gli esagoni, senza l'assimmetria dei coordini frastagliati.


Wow! Un voto negativo al netto per la pubblicazione di una libreria di codice funzionante, con esempi e documentazione, che risponde alla domanda / problema posto dal PO.
Pieter Geerkens,

5
Sebbene non fossi il downvoter (e l'etichetta generalmente suggerisce di lasciare un commento che spiega un downvote), sospetto che il downvote sia dovuto al fatto che (a) il post viene fuori dal suono della pubblicità e (b) mettendo la maggior parte di una risposta sull'altro il lato di un collegamento è generalmente disapprovato perché i collegamenti hanno la tendenza a marcire e i siti SE cercano di essere autonomi. Le informazioni fornite qui sono interessanti, ma non rispondono alla domanda dell'utente e le uniche informazioni che potrebbero rispondere alla domanda si trovano sull'altro lato del collegamento.
Steven Stadnicki,

Punti buoni; grazie. Ho ampliato il post con estratti che affrontano la questione di come gestire in modo efficiente più coordinate della griglia esadecimale. La libreria di codici pubblicata è freeware
Pieter Geerkens,

Oops! La divisione per 2 funziona solo per numeri interi positivi. (Grazie ancora, K&R.) Dovrebbe essere sostituito da una chiamata al metodo Normalize () in IntVector2D:
Pieter Geerkens,

public IntVector2D Normalize() { if (Z==1) return this; else { var x = (X >= 0) ? X : X - Z; var y = (Y >= 0) ? Y : Y - Z; return new IntVector2D(x/Z, y/Z); } }
Pieter Geerkens,

0

Questo è un problema risolto, con molta letteratura a sostegno. La migliore risorsa che conosco è su Red Blob Games: https://www.redblobgames.com/grids/hexagons/ .

In breve, il motivo più probabile è che hai iniziato con un sistema di coordinate errato. L'uso di un sistema di coordinate Cube che implementa l'algoritmo A * è abbastanza semplice. Guarda la demo dal vivo sul link sopra.

Se vuoi davvero usare qualche altro sistema, converti da e verso quando necessario.

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.