2013-03-19 6 views
7

Sono attualmente all'università per la programmazione di giochi per computer, nel processo di creazione di un motore fisico in C++.Problemi nell'introduzione di un timestep variabile

Mi è stato chiesto di introdurre un passo temporale tramite il metodo GetTickCount(), nonostante le imprecisioni, come è adatto per la profondità attuale che stiamo andando nel tempo passo

Il mio problema è che ora ho ha introdotto il movimento basato sul timestep, gli oggetti che sono stati disegnati sullo schermo attraverso due diversi override di una funzione di aggiornamento non sembrano funzionare.

Attraverso l'aggiunta di punti di interruzione nel codice, sembra che mElapsedTime non sembra aver passato alcun valore e sono perplesso.

Scusate se questo post è troppo prolisso o alcuni sono fuori tema, ho cercato di fornire più contesto possibile e questo è il mio primo post, ogni aiuto è apprezzato.

(parametro frameStartTime richiede solo un conteggio tick prima del fotogramma viene aggiornato e disegnato)

Edit: tempo mElapsed di tipo galleggiante, per facilitare la moltiplicazione negli aggiornamenti della classe delle particelle (es pos.x = velocità. x * timeStep) e questo è il motivo per cui il cast da un long senza segno a un float è ancora ridondante?

void Simulation::gameLoopDelay(DWORD frameStartTime) 
{ 
    DWORD presetFrameInterval = 16; 
    DWORD frameProcessingTime = GetTickCount() - frameStartTime; 

    if (frameProcessingTime < presetFrameInterval) 
    { 
     Sleep(presetFrameInterval - frameProcessingTime); 
    } 

    mElapsedTime = (float)frameProcessingTime/1000.0f; 
} 
+0

È una buona prima domanda. Un paio di note che ho sono che il tuo 'presetFrameInterval' dovrebbe essere una costante e che il cast di' float' nell'ultima riga è ridondante. 'MElapsedTime' a' float'? C'è un modo per prendere il codice pertinente e condensarlo in un campione completo solo con codice completamente pertinente? – chris

+0

Complimenti per aver provato a risolvere il problema da soli. Tuttavia, con questo frammento di codice non c'è molto che possiamo dirti. Non c'è nulla di evidentemente sbagliato qui. –

+0

La cosa più immediata che vedo potenzialmente errata è il presupposto che il tempo trascorso dopo che il sonno è accurato all'intervallo richiesto è passato a Sleep(). Assolutamente * non sarà così *. Non sai da quanto tempo sei uscito senza recuperare GetTickCount() dopo essere uscito dal sonno. (e oggetti di scena per la domanda, il suo ** lontano ** migliore della maggior parte delle prime domande, o anche delle domande non-prime. Hai provato ad analizzare e ad aiutare te stesso prima, che è * enorme *). – WhozCraig

risposta

1

Non sono assolutamente sicuro (perché non hai fornito abbastanza informazioni), ma il primo problema che vedo è che lo fai mElapsedTime = (float)frameProcessingTime/1000.0f; indipendentemente dallo Sleep(...). In altre parole, hai dimenticato di prendere in considerazione il sonno. Per fare ciò, è necessario recuperare nuovamente GetTickCount() dopo aver chiamato Sleep(...). Ma anche in questo caso, il tuo codice diventa troppo generico e soggetto a errori.

Inizio avvicinando il problema scrivendo piccole, flessibili e riutilizzabili classi che fanno compiti semplici:

class Stopwatch { 
public: 
    Stopwatch(): _start(0), _elapsed(0), _running(false) {} 

    void 
    start() { 
    if (_running) 
     return; 

    _running = true; 
    _start = GetTickCount(); 
    } 

    void 
    stop() { 
    if (!_running) 
     return; 

    _elapsed += GetTickCount() - _start; 
    _running = false; 
    } 

    void 
    reset() { 
    _running = false; 
    _elapsed = 0; 
    } 

    void 
    restart() { 
    _running = true; 
    _elapsed = 0; 
    _start = GetTickCount(); 
    } 

    bool 
    isRunning() const { 
    return _running; 
    } 

    DWORD 
    elapsed() const { 
    return _running ? _elapsed + (GetTickCount() - _start) : _elapsed); 
    } 

    bool 
    hasExpired(DWORD interval) const { 
    return elapsed() > interval; 
    } 

private: 
    DWORD _start; 
    DWORD _elapsed; 
    bool _running; 
}; 

E 'semplice come utilizzare questa classe per le vostre esigenze.

In secondo luogo, non capisco perché utilizzi lo Sleep. L'ultima volta che ho visto l'esempio del ritardo del fotogramma - era circa 5 anni fa in alcuni tutorial scadenti sullo scrivere giochi giocattolo. Questa roba era molto popolare nei vecchi giochi, ma nei giochi moderni non ha senso. Naturalmente se questo è il requisito per il tuo incarico, allora mi scuso, ma l'osservazione si applica ancora.

Infine, mi piacerebbe darvi ulteriori consigli sulla mia esperienza con la visualizzazione in tempo reale. Mai scrivere codice dipendente dal tempo con tali servizi grezzi come GetTickCount o altro popolare schifoso tra i programmatori API (o API Linux, non importa) di Windows, come ancora una volta profuma di tutorial obsoleti sulla creazione di giochi giocattolo.

Cosa usare quindi, chiedi?Bene, in questi giorni siamo fortunati perché ci sono librerie cross-platform ben progettate, affidabili, come ad esempio Boost. Probabilmente dovresti averne familiarità, tuttavia se non lo sei, dovresti assolutamente farlo. Per il tuo particolare problema, c'è un modulo Boost.Chrono che è stato attentamente progettato per funzionare correttamente nel tempo. L'ho usato in diversi progetti che coinvolgono il rendering in tempo reale, e devo ammettere che è molto robusto e vale la pena imparare.

Un altro, libreria - Boost.Units - ovvero deve per scrivere un motore fisico stabile e accurato. Questa libreria fornisce un supporto sicuro per tipo di unità (forza, massa, velocità, accelerazione, ecc.) E quantità per vari modelli di sistemi di unità (SI o CGS, ad esempio). Si basa sulla metaprogrammazione del modello e pertanto non genera un sovraccarico di runtime.

Ancora una volta, capisco che il tuo compito probabilmente non consente cose del genere. Tuttavia, credo che tutto ciò che ho menzionato qui sarebbe prezioso per te in futuro. A proposito, è un buon esempio di quanto dovresti sapere più di quanto gli incarichi universitari offrono per essere in grado di risolvere i problemi della vita reale.

Se desideri ancora maggiore assistenza sul problema in particolare, ho bisogno di ulteriori informazioni. Ad esempio, inizia a mostrarci l'intero codice del tuo ciclo di gioco.

+0

Grazie amico, molto apprezzato. Capisco il concetto di astrazione dei timer fuori tempo e cose come questa, e lo faccio anche nella mia programmazione (assegnazioni e simili), ma per questa specifica implementazione del tutorial mi è stato detto di implementarlo in questo modo, mi piacerebbe molto per mostrarti di più del progetto, ma penso che postare l'intero ciclo pubblicherebbe più codice di quello che penso possa essere appropriato per un sito come questo? Non ne sono sicuro. Grazie per il suggerimento sulla libreria Boost, ne ho sentito parlare prima ma non ancora verificato, ma penso che lo farò adesso. –

+0

Tu potresti postare solo quelle parti del ciclo di gioco che si occupano solo del tempo ** **, ad esempio quelle che coinvolgono 'frameStartTime', per esempio. Qui, su StackOverflow, ovviamente le persone non ti abbracceranno se pubblichi l'intero corpo della funzione ~ 200 linee. Dovresti essere in grado di estrarre le informazioni rilevanti dal tuo codice e pubblicare un esempio semplificato, ma allo stesso tempo dovrebbe riflettere perfettamente il tuo problema. L'abilità di ridurre il codice a scopo di presentazione è utile non solo per fare domande qui, ma anche per la produzione reale. –

1

Credo che, in fondo, è necessario modificare il codice per questo:

void Simulation::gameLoopDelay(DWORD frameStartTime) 
{ 
    DWORD presetFrameInterval = 16; 
    DWORD frameProcessingTime = GetTickCount() - frameStartTime; 

    if (frameProcessingTime < presetFrameInterval) 
    { 
     Sleep(presetFrameInterval - frameProcessingTime); 
    } 

    frameProcessingTime = GetTickCount() - frameStartTime; 

    mElapsedTime = (float)frameProcessingTime/1000.0f; 
} 

Questo non è il modo più consigliato di fare questo in quanto Sleep qui è probabilmente bloccando l'intero thread. Probabilmente è meglio fare solo il frame in un tempo frazionario, ma penso che questo risolverà il tuo codice.

+0

Grazie per la risposta, l'ho inserito dopo aver letto entrambe le risposte fornite e aver recuperato il numero di zecche ha funzionato, grazie mille e penso che probabilmente guarderò al tempo parziale per il mio incarico, anche se per i tutorial penso di sono vincolato a Sleep(). –