Una semplificazione comune è quella di comprimere il 3D in 2D. Anche se il rendering e la fisica possono essere veramente 3D, la logica decisionale non deve necessariamente trattare tutti e tre gli assi allo stesso modo. Le piste della MotoGP hanno poche colline, quindi la nostra IA è stata in grado di ignorare la componente y.
Successivamente, siamo passati dalle coordinate cartesiane x / z a un sistema relativo alla traccia. Le posizioni erano rappresentate da una coppia di valori:
int distance = quanto intorno alla traccia, memorizzato in formato punto fisso 16.16
- 0 = linea di partenza
- 0x8000 = a metà strada
- 0x10000 = riavviato all'inizio
- 0x1C000 = tre quarti del percorso nel secondo giro
float cross = quanto lateralmente attraverso la traccia 0 = sulla linea centrale
- -1 = bordo sinistro della superficie di corsa
- 1 = bordo destro della superficie di corsa
Per convertire tra questo e le coordinate cartesiane utilizzate dal nostro codice fisico e di rendering, abbiamo memorizzato un elenco di segmenti che definiscono la forma della superficie di corsa: struct TrackSegment {Vector CenterPoint; float DistanceToLeftEdge; float DistanceToRightEdge; }
Abbiamo creato diverse centinaia di queste strutture, distribuite uniformemente intorno alla traccia, tessellando le curve di Bezier da cui le tracce sono state originariamente create. Questo ci ha fornito informazioni sufficienti per scrivere le necessarie funzioni di conversione delle coordinate.
Con le coordinate relative alla traccia, molti calcoli utili diventano banalmente semplici:
if (abs(cross) > 1)
// You are off the track and should steer back toward the center line
if (this.distance > other.distance)
// You are ahead of the other player (even though you may be
// physically behind in 3D space if you have lapped them)
short difference = (short)(this.distance - other.distance);
if (abs(difference) < threshold)
// These two bikes are physically close together,
// so we should run obstacle avoidance checks
A causa del formato dei dati a virgola fissa, lanciare il contatore della distanza da 32 a 16 bit era un modo semplice per scartare il numero del giro, in modo da poter scegliere quali calcoli dovevano preoccuparsi se due bici si trovavano su giri diversi, invece di voler sapere se erano vicini nello spazio fisico. Grazie alla magia del complimento per due, trattare la differenza come un segno a 16 bit fornisce la distanza più breve indipendentemente da quale bici sia davanti (ricorda che in un sistema aritmetico modulo come una pista ciclabile ci sono due possibili distanze, come puoi misurare in in entrambe le direzioni intorno alla pista). Funziona anche quando le due bici si trovano ai lati opposti della linea di partenza, una situazione che richiederebbe una logica di casi speciali soggetta a errori nella maggior parte degli altri sistemi di coordinate.
Appiattire e raddrizzare questa area di gioco virtuale ha reso facile ragionare su cose come "Sono sulla linea di corsa?" o "Sto arrivando velocemente dietro quest'altra bici: ho più spazio per passarle a sinistra oa destra?" che sarebbe stato complicato da implementare in uno spazio mondiale 3D. Una volta deciso di passare a sinistra, convertiremmo le coordinate risultanti relative alla traccia nello spazio mondiale, a quel punto viene presa in considerazione la curvatura della traccia, mostrando come dovremmo guidare per raggiungere il nostro obiettivo prescelto.