Domanda

Ho letto Koen Witters Articolo dettagliato Informazioni su diverse soluzioni di loop di gioco, ma ho alcuni problemi che implementano l'ultimo con GLUT, che è quello raccomandato.

Dopo aver letto un paio di articoli, tutorial e codice da altre persone su come ottenere una costante velocità di gioco, penso che ciò che ho attualmente implementato (posterò il codice di seguito) è quello che Koen Witters ha chiamato Velocità del gioco dipendente da FPS variabile, il secondo nel suo articolo.

Innanzitutto, attraverso la mia esperienza di ricerca, ci sono un paio di persone che probabilmente hanno la conoscenza per dare una mano su questo, ma non so cosa sia il eccesso e cercherò di spiegare (sentiti libero di correggermi) le funzioni pertinenti per Il mio problema di questo toolkit OpenGL. Salta questa sezione se sai cos'è il eccesso e come giocarci.

GUTTRO SOLOKIT:

  • GLUT è un kit di strumenti OpenGL e aiuta con compiti comuni in OpenGL.
  • Il glutDisplayFunc(renderScene) Porta un puntatore a un renderScene() Callback della funzione, che sarà responsabile del rendering di tutto. Il renderScene() La funzione verrà chiamata solo una volta dopo la registrazione del callback.
  • Il glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0) Prende il numero di millisecondi da passare prima di chiamare il callback processAnimationTimer(). L'ultimo argomento è solo un valore da passare al callback del timer. Il processAnimationTimer() non verrà chiamato ciascuno TIMER_MILLISECONDS Ma solo una volta.
  • Il glutPostRedisplay() La funzione richiede un eccesso per rendere un nuovo frame, quindi dobbiamo chiamarlo ogni volta che cambiamo qualcosa nella scena.
  • Il glutIdleFunc(renderScene) potrebbe essere usato per registrare un callback a renderScene() (Questo non fa glutDisplayFunc() irrilevante) ma questa funzione dovrebbe essere evitata perché il callback inattivo viene continuamente chiamato quando gli eventi non vengono ricevuti, aumentando il carico della CPU.
  • Il glutGet(GLUT_ELAPSED_TIME) La funzione restituisce il numero di millisecondi da allora glutInit è stato chiamato (o prima chiamata a glutGet(GLUT_ELAPSED_TIME)). Questo è il timer che abbiamo con eccesso. So che ci sono migliori alternative per i timer ad alta risoluzione, ma per ora manteniamo con questo.

Penso che questa sia sufficiente informazioni su come le cornici di GLUT, quindi le persone che non lo sapevano potrebbero anche lanciare in questa domanda per cercare di aiutare se cadessero così.

Attuale implementazione:

Ora, non sono sicuro di aver implementato correttamente la seconda soluzione proposta da Koen, Velocità del gioco dipendente da FPS variabile. Il codice pertinente per questo va così:

#define TICKS_PER_SECOND 30
#define MOVEMENT_SPEED 2.0f

const int TIMER_MILLISECONDS = 1000 / TICKS_PER_SECOND;

int previousTime;
int currentTime;
int elapsedTime;

void renderScene(void) {
    (...)

    // Setup the camera position and looking point
    SceneCamera.LookAt();

    // Do all drawing below...

    (...)
}

void processAnimationTimer(int value) {
    // setups the timer to be called again
    glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0);

    // Get the time when the previous frame was rendered
    previousTime = currentTime;

    // Get the current time (in milliseconds) and calculate the elapsed time
    currentTime = glutGet(GLUT_ELAPSED_TIME);
    elapsedTime = currentTime - previousTime;

    /* Multiply the camera direction vector by constant speed then by the
       elapsed time (in seconds) and then move the camera */
    SceneCamera.Move(cameraDirection * MOVEMENT_SPEED * (elapsedTime / 1000.0f));

    // Requests to render a new frame (this will call my renderScene() once)
    glutPostRedisplay();
}

void main(int argc, char **argv) {
    glutInit(&argc, argv);

    (...)

    glutDisplayFunc(renderScene);

    (...)

    // Setup the timer to be called one first time
    glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0);
    // Read the current time since glutInit was called
    currentTime = glutGet(GLUT_ELAPSED_TIME);

    glutMainLoop();
}

Questa implementazione non è andata bene. Funziona nel senso che aiuta la velocità del gioco a dipendere costantemente dall'FPS. In modo che il passaggio dal punto A al punto B richieda lo stesso tempo, indipendentemente dal framerate alto/basso. Tuttavia, credo che sto limitando il framerate di gioco con questo approccio. [MODIFICARE: Ogni frame verrà reso solo quando viene chiamato il callback, ciò significa che il framerate sarà approssimativamente intorno TICKS_PER_SECOND fotogrammi al secondo. Questo non è giusto, non dovresti limitare il tuo potente hardware, è sbagliato. È la mia comprensione, però, che ho ancora bisogno di calcolare il elapsedTime. Solo perché sto dicendo a Glut di chiamare il callback del timer ogni TIMER_MILLISECONDS, non significa che lo farà sempre in tempo.

Non sono sicuro di come posso risolvere questo problema e ad essere completamente onesto, non ho idea di quale sia il gioco del gioco in eccesso, sai, il while( game_is_running ) Loop nell'articolo di Koen. [MODIFICARE: È la mia comprensione che il eccesso è evento guidato E quel loop di gioco inizia quando chiamo glutMainLoop() (che non ritorna mai), sì?

Pensavo di poter registrare un callback inattivo con glutIdleFunc() e usalo come sostituzione di glutTimerFunc(), rendering solo quando necessario (invece di tutto il tempo come al solito) ma quando l'ho testato con un callback vuoto (come void gameLoop() {}) E fondamentalmente non faceva nulla, solo uno schermo nero, la CPU è aumentata al 25% ed è rimasta lì fino a quando non ho ucciso il gioco e è tornato alla normalità. Quindi non penso che sia il percorso da seguire.

Usando glutTimerFunc() Non è sicuramente un buon approccio per eseguire tutti i movimenti/animazioni in base a questo, poiché sto limitando il mio gioco a un FPS costante, non bello. O forse lo sto usando male e la mia implementazione non è giusta?

Come posso esattamente avere una velocità di gioco costante con FPS variabile? Più esattamente, come impledo correttamente Koen Velocità di gioco costante con massimo FPS Soluzione (la quarta nel suo articolo) con eccesso? Forse questo non è affatto possibile con GLUT? In caso contrario, quali sono le mie alternative? Qual è l'approccio migliore a questo problema (costante velocità di gioco) con eccesso?

Modifica] Un altro approccio:

Ho sperimentato ed ecco cosa sono stato in grado di ottenere ora. Invece di calcolare il tempo trascorso su una funzione a tempo (che limita il framerate del mio gioco) lo sto facendo renderScene(). Ogni volta che le modifiche alla scena accadono, chiamo glutPostRedisplay() (cioè: telecamera che si muove, un po 'di animazione oggetto, ecc ...) che farà una chiamata a renderScene(). Posso usare il tempo trascorso in questa funzione per spostare la mia fotocamera, ad esempio.

Il mio codice ora si è trasformato in questo:

int previousTime;
int currentTime;
int elapsedTime;

void renderScene(void) {
    (...)

    // Setup the camera position and looking point
    SceneCamera.LookAt();

    // Do all drawing below...

    (...)
}

void renderScene(void) {
    (...)

    // Get the time when the previous frame was rendered
    previousTime = currentTime;

    // Get the current time (in milliseconds) and calculate the elapsed time
    currentTime = glutGet(GLUT_ELAPSED_TIME);
    elapsedTime = currentTime - previousTime;

    /* Multiply the camera direction vector by constant speed then by the
       elapsed time (in seconds) and then move the camera */
    SceneCamera.Move(cameraDirection * MOVEMENT_SPEED * (elapsedTime / 1000.0f));

    // Setup the camera position and looking point
    SceneCamera.LookAt();

    // All drawing code goes inside this function
    drawCompleteScene();

    glutSwapBuffers();

    /* Redraw the frame ONLY if the user is moving the camera
       (similar code will be needed to redraw the frame for other events) */
    if(!IsTupleEmpty(cameraDirection)) {
        glutPostRedisplay();
    }
}

void main(int argc, char **argv) {
    glutInit(&argc, argv);

    (...)

    glutDisplayFunc(renderScene);

    (...)

    currentTime = glutGet(GLUT_ELAPSED_TIME);

    glutMainLoop();
}

Conclusione, funziona, o almeno così sembra. Se non muovo la fotocamera, l'utilizzo della CPU è basso, nulla viene reso (ai fini del test ho solo una griglia che si estende per 4000.0f, mentre ZFAR è impostato su 1000.0f). Quando inizio a spostare la telecamera, la scena inizia a ridisegnare se stessa. Se continuo a premere i tasti di spostamento, l'utilizzo della CPU aumenterà; Questo è un comportamento normale. Scende quando smetto di muovermi.

A meno che non mi manchi qualcosa, per ora sembra un buon approccio. Ho trovato Questo interessante articolo Su IDEVGames e questa implementazione è probabilmente influenzata dal problema descritto in quell'articolo. Cosa ne pensi?

Si prega di notare che lo sto solo facendo per divertimento, non ho intenzione di creare un po 'di gioco da distribuire o qualcosa del genere, non nel prossimo futuro. Se lo facessi, probabilmente andrei con qualcos'altro oltre a Glut. Ma dal momento che sto usando GLUT e oltre al problema descritto su IDEVGames, pensi che quest'ultima implementazione sia sufficiente per il sovraccarico? L'unico vero problema a cui riesco a pensare in questo momento è che dovrò continuare a chiamare glutPostRedisplay() Ogni volta che la scena cambia qualcosa e continua a chiamarla fino a quando non c'è nulla di nuovo da ridisegnare. Penso che una piccola complessità ha aggiunto al codice per una causa migliore, credo.

Cosa ne pensi?

È stato utile?

Soluzione

Glut è progettato per essere Il gioco del gioco. Quando si chiama GlutMainLoop (), esegue un "per loop" senza condizioni di terminazione tranne il segnale EXIT (). Puoi implementare il tuo programma come stai facendo ora, ma hai bisogno di alcune modifiche minori. Innanzitutto, se vuoi sapere cos'è l'FPS, dovresti inserire quel monitoraggio nella funzione RendersCene (), non nella funzione di aggiornamento. Naturalmente, la funzione di aggiornamento viene chiamata più velocemente come specificato dal timer e stai trattando il tempo trascorso come una misura del tempo tra i frame. In generale, questo sarà vero perché chiami GlutpostredIsplay piuttosto lentamente e Glut non proverà ad aggiornare lo schermo se non è necessario (non è necessario ridisegnare se la scena non è cambiata). Tuttavia, ci sono altre volte in cui verrà chiamato Renderscene. Ad esempio, se trascini qualcosa attraverso la finestra. Se lo facessi, vedresti un FPS più elevato (se stavi monitorando correttamente l'FPS nella funzione di rendering).

Altri suggerimenti

Potresti usare glutIdleFunc, che si chiama continuamente ogni volta che è possibile-simile al while(game_is_running) ciclo continuo. Cioè, qualunque logica altrimenti metterai in questo while Loop, potresti mettere nel callback per glutIdleFunc. Puoi evitare di usare glutTimerFunc Tenendo traccia delle zecche da solo, come nell'articolo che hai collegato (usando glutGet(GLUT_ELAPSED_TIME)).

Ad esempio, una matrice di rotazione guidata dal mouse che si aggiorna a un frame rate fisso, indipendentemente dal tasso di frame di rendering. Nel mio programma, la barra spaziale attiva la modalità di benchmarking e determina i FXFP booleani.

Lascia andare il pulsante del mouse mentre ti trascini e puoi "lanciare" un oggetto trasformato da questa matrice.

Se FXFPS è vero, allora il rate frame di rendering viene strozzato per il tasso di frame di animazione; Altrimenti cornici identiche vengono disegnate ripetutamente per il benchmarking, anche se non sono passati abbastanza millisecondi per innescare qualsiasi animazione.

Se stai pensando di rallentare e accelerare i fotogrammi, devi riflettere attentamente sul fatto che tu intenda il rendering o le cornici di animazione in ogni caso. In questo esempio, il rendering di limitazione per animazioni semplici è combinato con l'accelerazione dell'animazione, per tutti i casi in cui i telai potrebbero essere abbandonati in un'animazione potenzialmente lenta.

Per accelerare l'animazione, le rotazioni vengono eseguite ripetutamente in un ciclo. Tale ciclo non è troppo lento rispetto all'opzione di fare trig con un angolo di rotazione adattivo; Fai solo attenzione a ciò che metti all'interno di qualsiasi ciclo che effettivamente richiede più tempo per eseguire, più basso è FPS. Questo ciclo richiede molto meno di un frame extra per essere completato, per ogni frame-drop che spiega, quindi è ragionevolmente sicuro.

int xSt, ySt, xCr, yCr, msM = 0, msOld = 0;
bool dragging = false, spin = false, moving = false;
glm::mat4 mouseRot(1.0f), continRot(1.0f);
float twoOvHght; // Set in reshape()
glm::mat4 mouseRotate(bool slow) {
    glm::vec3 axis(twoOvHght * (yCr - ySt), twoOvHght * (xCr - xSt), 0); // Perpendicular to mouse motion
    float len = glm::length(axis);
    if (slow) { // Slow rotation; divide angle by mouse-delay in milliseconds; it is multiplied by frame delay to speed it up later
        int msP = msM - msOld;
        len /= (msP != 0 ? msP : 1);
    }
    if (len != 0) axis = glm::normalize(axis); else axis = glm::vec3(0.0f, 0.0f, 1.0f);
    return rotate(axis, cosf(len), sinf(len));
}
void mouseMotion(int x, int y) {
    moving = (xCr != x) | (yCr != y);
    if (dragging & moving) {
        xSt = xCr; xCr = x; ySt = yCr; yCr = y; msOld = msM; msM = glutGet(GLUT_ELAPSED_TIME);
        mouseRot = mouseRotate(false) * mouseRot;
    }
}
void mouseButton(int button, int state, int x, int y) {
    if (button == 0) {
        if (state == 0) {
            dragging = true; moving = false; spin = false;
            xCr = x; yCr = y; msM = glutGet(GLUT_ELAPSED_TIME);
            glutPostRedisplay();
        } else {
            dragging = false; spin = moving;
            if (spin) continRot = mouseRotate(true);
        }
    }
}

E poi più tardi ...

bool fxFPS = false;
int T = 0, ms = 0;
const int fDel = 20;
void display() {
    ms = glutGet(GLUT_ELAPSED_TIME);
    if (T <= ms) { T = ms + fDel;
        for (int lp = 0; lp < fDel; lp++) {
            orient = rotY * orient; orientCu = rotX * rotY * orientCu; // Auto-rotate two orientation quaternions
            if (spin) mouseRot = continRot * mouseRot; // Track rotation from thowing action by mouse
        }
        orient1 = glm::mat4_cast(orient); orient2 = glm::mat4_cast(orientCu);
    }
    // Top secret animation code that will make me rich goes here
    glutSwapBuffers();
    if (spin | dragging) { if (fxFPS) while (glutGet(GLUT_ELAPSED_TIME) < T); glutPostRedisplay(); } // Fast, repeated updates of the screen
}

Divertiti a gettare le cose attorno a un asse; Trovo che la maggior parte delle persone lo faccia. Si noti che l'FPS non colpisce nulla, nell'interfaccia o nel rendering. Ho ridotto al minimo l'uso delle divisioni, quindi i confronti dovrebbero essere belli e accurati e qualsiasi imprecisione all'orologio non si accumula inutilmente.

La sincronizzazione dei giochi multiplayer è altre 18 conversazioni, giudicherei.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top