Sto lavorando con alcuni amici in un gioco basato su browser in cui le persone possono spostarsi su una mappa 2D. Sono passati quasi 7 anni e ancora la gente gioca a questo gioco, quindi stiamo pensando a un modo per dare loro qualcosa di nuovo. Da allora la mappa di gioco era un piano limitato e le persone potevano spostarsi da (0, 0) a (MAX_X, MAX_Y) con incrementi quantizzati X e Y (immaginatelo semplicemente come una grande scacchiera).
Crediamo che sia il momento di dargli un'altra dimensione, quindi, solo un paio di settimane fa, abbiamo iniziato a chiederci come potrebbe essere il gioco con altre mappature:
- Piano illimitato con movimento continuo: questo potrebbe essere un passo avanti ma non sono ancora convinto.
- Mondo toroidale (movimento continuo o quantizzato): sinceramente ho già lavorato con Torus ma questa volta voglio qualcosa di più ...
- Mondo sferico con movimento continuo: sarebbe fantastico!
Cosa vogliamo I browser degli utenti ricevono un elenco di coordinate come (latitudine, longitudine) per ciascun oggetto sulla mappa sferica della superficie; i browser devono quindi mostrarlo nella schermata dell'utente rendendoli all'interno di un elemento web (tela forse? questo non è un problema). Quando le persone fanno clic sul piano, convertiamo (mouseX, mouseY) in (lat, lng) e lo inviamo al server che deve calcolare un percorso tra la posizione dell'utente corrente al punto cliccato.
Quello che abbiamo Abbiamo iniziato a scrivere una libreria Java con molti utili calcoli matematici per lavorare con matrici di rotazione, quaternioni, angoli di Eulero, traduzioni, ecc. Abbiamo messo tutto insieme e creato un programma che genera punti sfera, li rende e li mostra all'utente all'interno di una JPanel. Siamo riusciti a catturare i clic e a tradurli in coordinate sferiche e a fornire alcune altre utili funzioni come rotazione della vista, ridimensionamento, traduzione ecc. Ciò che abbiamo ora è come un piccolo motore (davvero molto poco) che simula l'interazione client e server. Il lato client mostra i punti sullo schermo e rileva altre interazioni, il lato server rende la vista e fa altri calcoli come interpolare il percorso tra la posizione corrente e il punto cliccato.
Dov'è il problema? Ovviamente vogliamo avere il percorso più breve per interpolare tra i due punti del percorso . Usiamo i quaternioni per interpolare tra due punti sulla superficie della sfera e questo sembra funzionare bene fino a quando ho notato che non stavamo ottenendo il percorso più breve sulla superficie della sfera:
Tuttavia, il problema era che il percorso è calcolato come la somma di due rotazioni attorno agli assi X e Y. Quindi abbiamo cambiato il modo in cui calcoliamo il quaternione di destinazione: otteniamo il terzo angolo (il primo è la latitudine, il secondo è la longitudine, il terzo è la rotazione attorno al vettore che punta verso la nostra posizione attuale) che abbiamo chiamato orientamento. Ora che abbiamo l'angolo di "orientamento", ruotiamo l'asse Z e quindi usiamo il vettore del risultato come asse di rotazione per il quaternione di destinazione (puoi vedere l'asse di rotazione in grigio):
Quello che abbiamo ottenuto è il percorso corretto (puoi vederlo in un grande cerchio), ma ci arriviamo SOLO se il punto del percorso iniziale è a latitudine, longitudine (0, 0), il che significa che il vettore iniziale è (sphereRadius, 0 , 0). Con la versione precedente (immagine 1) non otteniamo un buon risultato anche quando il punto di partenza è 0, 0, quindi penso che ci stiamo muovendo verso una soluzione, ma la procedura che seguiamo per ottenere questo percorso è un po '"strana " può essere?
Nell'immagine seguente si ottiene una visione del problema che si presenta quando il punto di partenza non è (0, 0), come si può vedere il punto di partenza non è il vettore (sphereRadius, 0, 0) e come si può vedere il punto di destinazione (che è disegnato correttamente!) non è sulla rotta.
Il punto magenta (quello che giace sulla rotta) è il punto finale della rotta ruotato attorno al centro della sfera di (-startLatitude, 0, -startLongitude). Ciò significa che se calcolo una matrice di rotazione e la applico a tutti i punti del percorso, forse otterrò il percorso reale, ma comincio a pensare che esiste un modo migliore per farlo.
Forse dovrei provare a far passare il piano attraverso il centro della sfera e i punti del percorso, intersecarlo con la sfera e ottenere il geodetico? Ma come?
Mi dispiace di essere troppo prolisso e forse per un inglese scorretto ma questa cosa mi sta facendo impazzire!
EDIT: il codice qui sotto funziona bene! Grazie a tutti:
public void setRouteStart(double srcLat, double srcLng, double destLat, destLng) {
//all angles are in radians
u = Choords.sphericalToNormalized3D(srcLat, srcLng);
v = Choords.sphericalToNormalized3D(destLat, destLng);
double cos = u.dotProduct(v);
angle = Math.acos(cos);
if (Math.abs(cos) >= 0.999999) {
u = new V3D(Math.cos(srcLat), -Math.sin(srcLng), 0);
} else {
v.subtract(u.scale(cos));
v.normalize();
}
}
public static V3D sphericalToNormalized3D( double radLat, double radLng) {
//angles in radians
V3D p = new V3D();
double cosLat = Math.cos(radLat);
p.x = cosLat*Math.cos(radLng);
p.y = cosLat*Math.sin(radLng);
p.z = Math.sin(radLat);
return p;
}
public void setRouteDest(double lat, double lng) {
EulerAngles tmp = new AngoliEulero(
Math.toRadians(lat), 0, -Math.toRadians(lng));
qtEnd.setInertialToObject(tmp);
//do other stuff like drawing dest point...
}
public V3D interpolate(double totalTime, double t) {
double _t = angle * t/totalTime;
double cosA = Math.cos(_t);
double sinA = Math.sin(_t);
V3D pR = u.scale(cosA);
pR.sum(
v.scale(sinA)
);
return pR;
}