Comprensione dei comportamenti dello sterzo evitare le collisioni

La decente navigazione NPC richiede spesso la possibilità di evitare ostacoli. Questo tutorial copre il evitare le collisioni comportamento di sterzata, che consente ai personaggi di evitare con garbo qualsiasi numero di ostacoli nell'ambiente.

Nota: Sebbene questo tutorial sia scritto usando AS3 e Flash, dovresti essere in grado di utilizzare le stesse tecniche e concetti in quasi tutti gli ambienti di sviluppo di giochi. Devi avere una conoscenza di base dei vettori di matematica.


introduzione

L'idea alla base di evitare le collisioni è di generare una forza di governo per evitare gli ostacoli ogni volta che si è abbastanza vicini da bloccare il passaggio. Anche se l'ambiente ha diversi ostacoli, questo comportamento userà uno di essi alla volta per calcolare la forza di evitamento.

Vengono analizzati solo gli ostacoli che precedono il personaggio; il più vicino, ritenuto il più minaccioso, viene selezionato per la valutazione. Di conseguenza il personaggio è in grado di schivare tutti gli ostacoli nell'area, passando da uno all'altro con grazia e senza soluzione di continuità.


Gli ostacoli davanti al personaggio vengono analizzati e viene selezionato quello più vicino (il più minaccioso).

Il comportamento di evitare le collisioni non è un algoritmo di individuazione dei percorsi. Farà muovere i personaggi attraverso l'ambiente, evitando gli ostacoli, alla fine trovando una via per passare attraverso i blocchi - ma non funziona molto bene con gli ostacoli "L" o "T", ad esempio.

Mancia: Questo comportamento di evitare le collisioni può sembrare simile al comportamento di fuga, ma c'è una differenza importante tra di loro. Un personaggio che si muove vicino a un muro lo eviterà solo se bloccherà la strada, ma il comportamento di fuggire allontanerà sempre il personaggio dal muro.

Vedendo avanti

Il primo passo per evitare gli ostacoli nell'ambiente è percepirli. Gli unici ostacoli che il personaggio deve preoccupare sono quelli che gli stanno di fronte e bloccano direttamente la rotta corrente.

Come precedentemente spiegato, il vettore di velocità descrive la direzione del personaggio. Sarà usato per produrre un nuovo vettore chiamato avanti, che è una copia del vettore di velocità, ma con una lunghezza diversa:


Il avanti il vettore è la linea di vista del personaggio.

Questo vettore è calcolato come segue:

 avanti = posizione + normalizza (velocità) * MAX_SEE_AHEAD

Il avanti lunghezza del vettore (regolata con MAX_SEE_AHEAD) definisce quanto lontano il personaggio "vedrà".

Il più grande MAX_SEE_AHEAD è, prima il personaggio inizierà a recitare per schivare un ostacolo, perché lo percepirà come una minaccia anche se è lontano:


Maggiore è la lunghezza avanti, più presto il personaggio inizierà a comportarsi per schivare un ostacolo.

Controllo della collisione

Per controllare la collisione, ogni ostacolo (o il suo riquadro di delimitazione) deve essere descritto come una forma geometrica. L'uso di una sfera (cerchio in due dimensine) dà i migliori risultati, quindi ogni ostacolo nell'ambiente sarà descritto come tale.

Una possibile soluzione per verificare la collisione è l'intersezione della linea-sfera: la linea è la avanti vettore e la sfera è l'ostacolo. Questo approccio funziona, ma userò una semplificazione di ciò che è più facile da capire e ha risultati simili (anche migliori a volte).

Il avanti il vettore verrà utilizzato per produrre un altro vettore con metà della sua lunghezza:


Stessa direzione, metà della lunghezza.

Il ahead2 il vettore è calcolato esattamente come avanti, ma la sua lunghezza è dimezzata:

 avanti = posizione + normalizza (velocità) * MAX_SEE_AHEAD ahead2 = position + normalize (velocity) * MAX_SEE_AHEAD * 0.5

Vogliamo eseguire un controllo di collisione per verificare se uno di questi due vettori si trova all'interno della sfera di ostacolo. Questo si ottiene facilmente confrontando la distanza tra la fine del vettore e il centro della sfera.

Se la distanza è inferiore o uguale al raggio della sfera, il vettore si trova all'interno della sfera e viene rilevata una collisione:


Il vettore avanti intercetta l'ostacolo se d < r. The ahead2 vector was omitted for clarity.

Se o dei due vettori avanti sono all'interno della sfera di ostacolo, quindi quell'ostacolo sta bloccando la strada. La distanza euclidea tra due punti può essere usata:

 distanza funzione privata (a: Object, b: Object): Number return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));  private function lineIntersectsCircle (avanti: Vector3D, ahead2: Vector3D, obstacle: Circle): Boolean // la proprietà "center" dell'ostacolo è una Vector3D. distanza di ritorno (obstacle.center, avanti) <= obstacle.radius || distance(obstacle.center, ahead2) <= obstacle.radius; 

Se più di un ostacolo blocca la strada, viene selezionato per il calcolo quello più vicino (il "più minaccioso"):


L'ostacolo più vicino (il più pericoloso) è selezionato per il calcolo.

Calcolo della Forza da evitare

La forza di evitamento deve allontanare il personaggio dall'ostacolo, permettendogli di schivare la sfera. Può essere fatto usando un vettore formato usando il centro della sfera (che è un vettore di posizione) e il avanti vettore. Calcoliamo questa forza di evitamento come segue:

 avoidance_force = ahead - obstacle_center avoidance_force = normalize (avoidance_force) * MAX_AVOID_FORCE

Dopo avoidance_force è calcolato è normalizzato e ridimensionato da MAX_AVOID_FORCE, che è un numero usato per definire il avoidance_force lunghezza. Il più grande MAX_AVOID_FORCE è, più forte è la forza di evitamento che allontana il personaggio dall'ostacolo.


Calcolo della forza di elusione. La linea tratteggiata arancione mostra il percorso che il personaggio intraprenderà per evitare l'ostacolo. Mancia: La posizione di qualsiasi entità può essere descritta come un vettore, in modo che possano essere utilizzati nei calcoli con altri vettori e forze.

Evitando l'ostacolo

L'implementazione finale per il collisionAvoidance () metodo, che restituisce la forza di evitamento, è:

 private function collisionAvoidance (): Vector3D ahead = ...; // calcola il vettore avanti ahead2 = ...; // calcola il vettore ahead2 var mostThreatening: Obstacle = findMostThreateningObstacle (); var avoidance: Vector3D = new Vector3D (0, 0, 0); if (mostThreatening! = null) avoidance.x = ahead.x - mostThreatening.center.x; avoidance.y = ahead.y - mostThreatening.center.y; avoidance.normalize (); avoidance.scaleBy (MAX_AVOID_FORCE);  else avoidance.scaleBy (0); // annulla la forza di evitamento evitamento di ritorno;  funzione privata findMostThreateningObstacle (): Obstacle var mostThreatening: Obstacle = null; per (var i: int = 0; i < Game.instance.obstacles.length; i++)  var obstacle :Obstacle = Game.instance.obstacles[i]; var collision :Boolean = lineIntersecsCircle(ahead, ahead2, obstacle); // "position" is the character's current position if (collision && (mostThreatening == null || distance(position, obstacle) < distance(position, mostThreatening)))  mostThreatening = obstacle;   return mostThreatening; 

La forza di evitamento deve essere aggiunta al vettore di velocità del personaggio. Come precedentemente spiegato, tutte le forze di guida possono essere combinate in una, producendo una forza che rappresenta tutto il comportamento attivo che agisce sul personaggio.

A seconda dell'angolo e della direzione della forza di prevenzione, non interromperà altre forze di guida, come la ricerca o la deviazione. La forza di evitamento viene aggiunta alla velocità del giocatore come al solito:

 steering = nothing (); // il vettore nullo, che significa sterzata "forza zero" = sterzo + ricerca (); // supponendo che il personaggio stia cercando qualcosa di sterzo = sterzo + collisione Evidenza (); sterzo = troncato (sterzo, max_force) sterzata = sterzo / velocità di massa = troncata (velocità + sterzata, max_velocità) posizione = posizione + velocità

Poiché tutti i comportamenti di guida vengono ricalcolati ad ogni aggiornamento del gioco, la forza di evitamento rimarrà attiva fintanto che l'ostacolo bloccherà il percorso.

Non appena l'ostacolo non intercetta il avanti linea vettoriale, la forza di evitamento diventerà nulla (nessun effetto) o sarà ricalcolata per evitare il nuovo ostacolo minaccioso. Il risultato è un personaggio in grado di evitare gli ostacoli:


Muovi il cursore del mouse. Clicca per mostrare le forze.

Miglioramento del rilevamento delle collisioni

L'implementazione attuale ha due problemi, entrambi relativi al rilevamento delle collisioni. Il primo succede quando il avanti i vettori sono fuori dalla sfera degli ostacoli, ma il personaggio è troppo vicino (o all'interno) all'ostacolo.

Se ciò accade, il personaggio toccherà (o inserirà) l'ostacolo, saltando il processo di evitamento perché non è stata rilevata alcuna collisione:


A volte il avanti i vettori sono fuori dall'ostacolo, ma il personaggio è dentro.

Questo problema può essere risolto aggiungendo un terzo vettore al controllo di collisione: il vettore di posizione del personaggio. L'uso di tre vettori migliora notevolmente il rilevamento delle collisioni.

Il secondo problema si verifica quando il personaggio è vicino all'ostacolo, allontanandolo da esso. A volte le manovre causano una collisione, anche se il personaggio sta semplicemente ruotando per affrontare un'altra direzione:


La manovra potrebbe causare una collisione, anche se il personaggio sta ruotando.

Questo problema può essere risolto ridimensionando il file avanti vettori in base alla velocità attuale del personaggio. Il codice per calcolare il avanti il vettore, ad esempio, è cambiato in:

 dynamic_length = length (velocity) / MAX_VELOCITY ahead = position + normalize (velocity) * dynamic_length

La variabile dynamic_length andrà da 0 a 1. Quando il personaggio si muove a tutta velocità, dynamic_length è 1; quando il personaggio sta rallentando o accelerando, dynamic_length è 0 o superiore (ad esempio 0,5).

Di conseguenza, se il personaggio sta semplicemente manovrando senza muoversi, dynamic_length tende a zero, producendo un nulla avanti vettore, che non ha collisioni.

Di seguito è riportato il risultato con questi miglioramenti:


Muovi il cursore del mouse. Clicca per mostrare le forze.

Demo: è Zombie Time!

Per dimostrare il comportamento di evitare collisioni in azione, penso che un'orda di zombi sia la soluzione perfetta. Di seguito una demo che mostra diversi zombi (con diverse velocità) alla ricerca del cursore del mouse. Art di SpicyPixel e Clint Bellanger, di OpenGameArt.


Muovi il cursore del mouse. Clicca per mostrare le forze.

Conclusione

Il comportamento di evitare le collisioni consente a qualsiasi personaggio di evitare gli ostacoli nell'ambiente. Dal momento che tutte le forze di guida vengono ricalcolate ad ogni aggiornamento di gioco, i personaggi interagiscono perfettamente con diversi ostacoli, analizzando sempre il più minaccioso (il più vicino).

Anche se questo comportamento non è un algoritmo di individuazione dei percorsi, i risultati ottenuti sono abbastanza convincenti per le mappe affollate.