L'estrapolazione interrompe il rilevamento delle collisioni


10

Prima di applicare l'estrapolazione al movimento del mio sprite, la mia collisione ha funzionato perfettamente. Tuttavia, dopo aver applicato l'estrapolazione al movimento del mio sprite (per appianare le cose), la collisione non funziona più.

Ecco come funzionavano le cose prima dell'estrapolazione:

inserisci qui la descrizione dell'immagine

Tuttavia, dopo aver implementato la mia estrapolazione, la routine di collisione si interrompe. Suppongo che ciò avvenga perché agisce sulla nuova coordinata che è stata prodotta dalla routine di estrapolazione (che si trova nella mia chiamata di rendering).

Dopo aver applicato la mia estrapolazione

inserisci qui la descrizione dell'immagine

Come correggere questo comportamento?

Ho provato a mettere un controllo extra di collisione subito dopo l'estrapolazione - questo sembra chiarire molti problemi ma l'ho escluso perché mettere la logica nel mio rendering è fuori discussione.

Ho anche provato a fare una copia della posizione di spritesX, estrapolandola e disegnandola usando quella piuttosto che l'originale, lasciando così l'originale intatto per la logica da prendere in considerazione - questa sembra un'opzione migliore, ma produce ancora alcuni strani effetti quando si scontrano con i muri. Sono abbastanza sicuro che anche questo non sia il modo corretto di affrontarlo.

Ho trovato un paio di domande simili qui ma le risposte non mi hanno aiutato.

Questo è il mio codice di estrapolazione:

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

Applicazione dell'estrapolazione

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

modificare

Come ho detto sopra, ho provato a fare una copia delle coordinate dello sprite appositamente per disegnare con ... questo ha i suoi problemi.

In primo luogo, indipendentemente dalla copia, quando lo sprite si muove, è super-liscio, quando si ferma, traballa leggermente a sinistra / a destra - poiché sta ancora estrapolando la sua posizione in base al tempo. È un comportamento normale e possiamo 'disattivarlo' quando lo sprite si ferma?

Ho provato ad avere flag per sinistra / destra e estrapolare solo se uno di questi è abilitato. Ho anche provato a copiare le ultime e attuali posizioni per vedere se c'è qualche differenza. Tuttavia, per quanto riguarda la collisione, questi non aiutano.

Se l'utente preme dire, il pulsante destro e lo sprite si stanno muovendo a destra, quando colpisce un muro, se l'utente continua a tenere premuto il pulsante destro, lo sprite continuerà ad animarsi a destra, mentre viene bloccato dal muro ( quindi in realtà non si muove), tuttavia poiché la bandiera destra è ancora impostata e anche perché la routine di collisione sposta costantemente lo sprite fuori dal muro, appare ancora al codice (non al giocatore) che lo sprite si sta ancora muovendo, e quindi l'estrapolazione continua. Quindi, ciò che il giocatore vedrebbe, è lo sprite "statico" (sì, si sta animando, ma in realtà non si muove attraverso lo schermo), e ogni tanto si agita violentemente mentre l'estrapolazione tenta di fare la sua cosa ..... .. Spero che questo aiuto


1
Questo richiederà un po 'di digestione per comprendere appieno ("interpolazione" ha apparentemente una dozzina di significati diversi e non è del tutto chiaro a prima vista solo cosa intendi con questo qui), ma il mio primo istinto è "non dovresti fare nulla per influenzare il tuo posizione dell'oggetto nella routine di rendering ". Il tuo renderer dovrebbe disegnare il tuo oggetto nella posizione data dall'oggetto, e manipolando l'oggetto c'è una ricetta per il problema, dal momento che accoppia intrinsecamente il renderer alla fisica del gioco. In un mondo ideale, il tuo renderer dovrebbe essere in grado di prendere i puntatori const sugli oggetti di gioco.
Steven Stadnicki,

Ciao @StevenStadnicki, molte grazie per il tuo commento, ci sono una moltitudine di esempi che mostrano un valore di interpolazione passato nel renderer, per favore vedi questo: mysecretroom.com/www/programming-and-software/… che proviene da dove ho adattato il mio codice. La mia comprensione limitata è che questo interpola la posizione tra l'ultimo e il prossimo aggiornamento in base alla quantità di tempo impiegata dall'ultimo aggiornamento - sono d'accordo che è un po 'un incubo! Le sarei grato se potessi proporre un'alternativa con cui potrei lavorare più facilmente - evviva :-)
BungleBonce

6
Bene, dirò "solo perché puoi trovare il codice per qualcosa non lo rende una buona pratica". :-) In questo caso, tuttavia, sospetto che la pagina a cui ti sei collegato utilizza il valore di interpolazione per capire dove visualizzare i suoi oggetti, ma in realtà non aggiorna le posizioni degli oggetti con loro; puoi farlo anche solo avendo una posizione specifica per il disegno che viene calcolata ogni fotogramma, ma mantenendo quella posizione separata dalla posizione "fisica" dell'oggetto.
Steven Stadnicki,

Ciao @StevenStadnicki, come indicato nella mia domanda (il paragrafo che inizia "Ho anche provato a fare una copia"), in realtà ho già tentato di utilizzare una posizione "solo disegno" :-) Puoi proporre un metodo di interpolazione in cui io non è necessario apportare modifiche alla posizione dello sprite all'interno della routine di rendering? Grazie!
BungleBonce,

Guardando il tuo codice sembra che tu stia facendo l'estrapolazione invece dell'interpolazione.
Durza007,

Risposte:


1

Non posso ancora pubblicare un commento, quindi lo posterò come risposta.

Se capisco correttamente il problema, va qualcosa del genere:

  1. prima hai una collisione
  2. quindi la posizione dell'oggetto viene corretta (presumibilmente dalla routine di rilevamento delle collisioni)
  3. la posizione aggiornata dell'oggetto viene inviata alla funzione di rendering
  4. la funzione di rendering aggiorna quindi la posizione dell'oggetto mediante estrapolazione
  5. la posizione dell'oggetto estrapolato ora interrompe la routine di rilevamento delle collisioni

Mi vengono in mente 3 possibili soluzioni. Li elencherò nell'ordine che è più desiderabile per lo meno IMHO.

  1. Sposta l'estrapolazione fuori dalla funzione di rendering. Estrapolare la posizione dell'oggetto e quindi verificare una collisione.
  2. o se si desidera mantenere l'estrapolazione nella funzione di rendering, impostare un flag per indicare che si è verificata una collisione. Chiedi al rilevamento delle collisioni di correggere la posizione dell'oggetto come già fai, ma prima di calcolare il valore di estrapolazione controlla prima il flag di collisione. Poiché l'oggetto è già dove deve essere, non è necessario spostarlo.
  3. L'ultima possibilità, che per me è più una soluzione alternativa che una correzione sarebbe quella di compensare eccessivamente il rilevamento delle collisioni. Dopo una collisione, allontanare l'oggetto dal muro, in modo che dopo l'estrapolazione l'oggetto sia di nuovo al muro.

Codice di esempio per # 2.

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

Penso che il numero 2 sia probabilmente il più veloce e semplice per iniziare una corsa, anche se il n. 1 sembra essere una soluzione logicamente più accurata. A seconda di come gestisci la tua delta time, la soluzione n. 1 potrebbe essere interrotta in modo analogo da un grande delta, nel qual caso potresti dover usare sia il n. 1 che il n. 2 insieme.

EDIT: ho frainteso il tuo codice prima. I loop hanno lo scopo di renderizzare il più velocemente possibile e aggiornarsi a intervalli prestabiliti. Questo è il motivo per cui dovresti interpolare la posizione dello sprite, per gestire il caso in cui stai disegnando più dell'aggiornamento. Tuttavia, se il ciclo rimane indietro, si esegue il polling sull'aggiornamento fino a quando non si è raggiunti o si è saltato il numero massimo di disegni del fotogramma.

Detto questo, l'unico problema è che l'oggetto si sta muovendo dopo una collisione. In caso di collisione l'oggetto dovrebbe smettere di muoversi in quella direzione. Quindi, se c'è una collisione, imposta la sua velocità su 0. Ciò dovrebbe impedire alla funzione di rendering di spostare ulteriormente l'oggetto.


Ciao @Aholio Ho provato l'opzione 2 e funziona ma causa alcuni problemi forse ho sbagliato, lo rivedrò. Tuttavia sono molto interessato all'opzione 1, anche se non riesco a trovare informazioni su come eseguire questa operazione. Come posso estrapolare dire nella mia routine logica? A proposito il mio delta è fisso, quindi è 1/60 o 0,01667 (piuttosto questa è la cifra che uso per integrare anche se il tempo che impiega ogni iterazione del mio gioco può ovviamente variare a seconda di ciò che sta succedendo ma non dovrebbe mai richiedere più di 0,01667 ) quindi qualsiasi aiuto su questo sarebbe un grande grazie.
BungleBonce,

Forse non capisco perfettamente cosa stai facendo. Qualcosa che mi sembra un po 'strano è che non stai solo facendo movimenti di posizione nella tua funzione di disegno; stai anche facendo calcoli del tempo. Questo è fatto per ogni oggetto? L'ordine corretto dovrebbe essere: calcolo del tempo eseguito nel codice del loop di gioco. Il loop di gioco passa il delta in una funzione di aggiornamento (dt). Aggiorna (dt) aggiornerà tutti gli oggetti di gioco. Dovrebbe gestire qualsiasi movimento, estrapolazione e rilevamento delle collisioni. Dopo che update () torna al loop di gioco, chiama quindi una funzione render () che disegna tutti gli oggetti di gioco appena aggiornati.
Aholio,

Hmmmmm al momento la mia funzione di aggiornamento fa tutto. Fa il movimento (per movimento intendo calcola la nuova posizione degli sprite - questo è calcolato dal mio tempo delta). L'unica cosa che sto facendo nella mia funzione di rendering (a parte il disegno reale) è estrapolare la posizione "nuova", ma ovviamente questo utilizza il tempo in quanto deve tener conto del tempo dall'ultimo aggiornamento logico alla chiamata di rendering. Questa è la mia comprensione comunque :-) Come lo faresti? Non capisco come utilizzare l'estrapolazione all'interno dell'aggiornamento logico. Grazie!
BungleBonce,

Aggiornato la mia risposta. Spero che aiuti
Aholio il

Grazie, vedi nella mia Q originale "quando si ferma, oscilla leggermente a sinistra / a destra" - quindi anche se interrompo il mio sprite, l'estrapolazione mantiene comunque lo sprite "in movimento" (traballante) - questo accade perché non sto usando il Le posizioni vecchie e attuali di sprite per elaborare la mia estrapolazione, quindi anche se lo sprite è statico, ha ancora questo effetto oscillante basato sul valore di "estrapolazione" che viene elaborato per ogni iterazione di frame. Ho anche provato a dire "se la posizione vecchia e quella attuale sono uguali, allora non estrapolare", ma questo non sembra funzionare, tornerò indietro e darò un'altra occhiata!
BungleBonce,

0

Sembra che sia necessario separare completamente il rendering e l'aggiornamento della fisica. Di solito la simulazione sottostante verrà eseguita a intervalli di tempo discreti e la frequenza non cambierà mai. Ad esempio, potresti simulare il movimento della tua palla ogni 1/60 di secondo, e basta.

Al fine di consentire un framerate variabile, il codice di rendering dovrebbe operare su una frequenza variabile, ma qualsiasi simulazione dovrebbe comunque essere a un intervallo di tempo fisso. Ciò consente alla grafica di leggere la memoria della simulazione in sola lettura e di impostare l'interpolazione anziché l'estrapolazione.

Poiché l'estrapolazione sta cercando di prevedere dove saranno i valori futuri, improvvisi cambiamenti nei movimenti possono darti enormi errori di estrapolazione. È preferibile invece eseguire il rendering della scena su un fotogramma dietro la simulazione e interpolare tra posizioni note discrete.

Se vuoi vedere alcuni dettagli di implementazione ho già scritto una breve sezione su questo argomento in un articolo qui . Si prega di consultare la sezione chiamata "Timestepping".

Ecco l'importante codice psuedo dall'articolo:

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

La RenderGamefunzione è di maggior interesse. L'idea è di utilizzare l'interpolazione tra posizioni di simulazione discrete. Il codice di rendering può creare copie proprie dei dati di sola lettura della simulazione e utilizzare un valore interpolato temporaneo per il rendering. Questo ti darà un movimento molto fluido senza problemi sciocchi come quello che sembra avere!

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.