DOMANDA C++| Loop a 60 FPS

Marcus Aseth

Utente Attivo
404
138
OS
Windows 10
Stavo un attimo sperimentando con std::chrono cercando di creare un game loop a 60 FPS.
Quello sotto è quello che ho, mi domandavo però se fosse il modo ideale di farlo o se qualcuno ha qualche suggerimento al riguardo :P

C:
#include <iostream>
#include <chrono>
#include <cstdint>
using namespace std;
using namespace chrono;

using frame = duration<int32_t, ratio<1, 60>>;
using ms = duration<float, milli>;

int main()
{
    time_point<steady_clock> fpsTimer(steady_clock::now());
    frame FPS{};

    while (true)
    {
        FPS = duration_cast<frame>(steady_clock::now() - fpsTimer);
        if (FPS.count() >= 1)
        {
            fpsTimer = steady_clock::now();
            cout << "LastFrame: " << duration_cast<ms>(FPS).count() << "ms  |  FPS: " << FPS.count() * 60 << endl;
        }
    }
    return 0;
}



Output:
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
LastFrame: 16.6667ms | FPS: 60
 
Ultima modifica:

Nico911

Utente Attivo
192
13
Non sono molto pratico della libreria chronocast quindi dimmi se ho frainteso dei passaggi.

Tu inizi il programma impostando fpstimer presumo con l'ora attuale, a quel punto lasci trascorrere il ciclo infinito, quando il tempo attuale sottratto all'ora iniziale supera un certo tick(che non ho trovato dove è specificato) tu mandi il messaggio a schermo e reimposti fpstimer.
Se ho interpretato bene,
1)ti viene sempre 60+ fps che non è corretto gli fps sono proporzionali a 1s/tempo trascorso
2)se lo scopo è di creare un programma che vada costante a 60fps sei completamente fuori strada(il tuo timer è legato ai tempi del programma, se ci mette 5s a fare un ciclo un frame ti dura 5s e non vai più a 60FPS)
 

Marcus Aseth

Utente Attivo
404
138
OS
Windows 10
http://www.cplusplus.com/reference/chrono/steady_clock/

Sotto "Clock Properties" dice:
steady
Every tick the clock advances takes the same amount of time (in terms of physical time).


Lo steady clock non è legato al mio programma, ed il tipo da me definito "frame" è definito in termini di 1/60 secondi, percui quando il (tempo attuale - tempo precedente) è superiore ad 1 sessantesimo di secondo (tempo reale) allora il programma fà l'update dentro l' if statement.
 
Ultima modifica:

Nico911

Utente Attivo
192
13
Il calcolo dell'ora sono d'accordo, quello va bene ma il vincolo del programma c'è sempre, il programma calcola il tempo alla prima istruzione del ciclo ed è li che è vincolato ai tempi del programma, chi ti dice che ti faccia solo un tick tra quando imposti fpsTimer e quando a inizio ciclo ricalcoli il tempo trascorso? In quel caso si, non c'è codice pesante quindi ci mette sempre più o meno lo stesso tempo, ma se ci fosse, magari ti uscirebbe dal ciclo con tick superiori(2,5,10 vanno tutti bene per la tua condizione).
Quello lo risolvi agevolmente modificando nel cout quel FPS.count()*60 in 60/FPS.count()
(Così come è, è sbagliato, è alla rovescia, se vai più lento ti aumentano gli FPS invece si calare)
In realtá non userei il count direttamente ma se ppssibile utilizzerei tutti il valore(ms compresi) per arrivare a un FPS completo(così ti da 60,30,15,etc.. Non ti dara mai la misura precisa ad esempio 36.47FPS per dire un numero)
Questo se vuoi fare un piccolo programma che calcola gli fps di un ciclo, se l'intenzione è invece creare un ciclo che indipendentemente dal peso del codice giri a 60fps sei fuori strada perchè devi come dicevo svincolare la gestione del frame rate dal programma principale.(non puoi a cose fatte quando il tempo è trascorso dire ehh no hai superato il tempo massimo, ormai non torni più indietro)
 
  • Mi piace
Reazioni: Marcus Aseth

Marcus Aseth

Utente Attivo
404
138
OS
Windows 10
Poi mi metterò a farlo meglio in futuro quando posso prestargli piena attenzione, al momento sono impegnato da una settimana fà nella sfida di completare la sezione matematica di Khan Academy al 100%, 6 ore di video e 2-3 di pratica al giorno e nonostante questo mi sà che prenderà ancora qualche mese... :D
 

Nico911

Utente Attivo
192
13
Eh auguri sono nella stessa situazione con la certificazione di ingegnere progettista di reti cisco ma non mi pesa molto, il problema se te la fai di corsa è se alla fine del malloppone ti ricorderai qualcosa o dovrai ricominciare d'accapo :D
 

Marcus Aseth

Utente Attivo
404
138
OS
Windows 10
Chiaro, il mio piano è di mettere in pratica quello che imparo con esercizi, esempio su https://codefights.com/ o project Euler su https://www.hackerrank.com, tipo ieri per risolvere questa sfida qua -> https://codefights.com/arcade/intro/level-2/yuGuHvcCaFCKk56rJ ho riempito un foglio di ragionamenti fino a trovare la soluzione senza loop in una sola riga:
CSS:
int shapeArea(int n) {
   return 2*(n*(n-1))+1;
}
magari questo esempio è un pò troppo semplice ma sono ugualmente fiero di essere riuscito ad usare la matematica come uno strumento per risolvere un problema e non semplicemente come una formula magica che qualcuno mi ha passato :D
 

Nico911

Utente Attivo
192
13
È il bello dell'iniziare informatica, quando ancora basi tutto sulla logica e la capacitá di risolvere un problema ti emoziona, quando fai i programmini e vedi che funzionano come volevi tu, fai bene a farli, man mano migliora la tua capacitá di analisi, mi ricordi me il primo anno all'universitá, che li usavo come giochi passatempo, Stai per iniziare informatica/ing.informatica?
 

Nico911

Utente Attivo
192
13
Ah bene bene ;) , ma da autodidatta non so quanto più in la potrai arrivare, le basi, utilizzare linguaggi come il c++, java, mysql etc.. e crearci applicazioni logiche ce la fai tranquillamente( in pratica il corso di fondamenti di informatica I di un universitá di informatica), ma per andare più avanti ti servono molte discipline, difficile che riesci a farti da solo una buona conoscenza(non perchè non si può, io l'universitá l'ho fatta da non frequentante usando solo i libri ma perchè non sai dove andare a parare)
 

Marcus Aseth

Utente Attivo
404
138
OS
Windows 10
Sto di nuovo con chrono, ecco la mia nuova versione del game loop :P

C++:
void App::GameLoop()
{
    while (Game->IsRunning())
    {
        //UPDATE
        while (Timer->CurrentTime() > Timer->NextUpdateTime)
        {
            Game->Update(Timer->UpdateTick());
        }
        //DRAW
        Game->Draw(Timer->DrawTick());
    }
}
E la mia classe Timer, sperando non sia buggata... xD
C++:
#pragma once
#include <chrono>
#include <string>
using namespace std::chrono;

//This type sets Ticks per second of the update call
template <size_t TickPerSec>
using UpdateTickDuration = duration<uint32_t, std::ratio<1, TickPerSec>>;

using FloatSeconds = duration<float>;

template<size_t TickPerSec>
class GameTimer
{
private:/*variables*/
    UpdateTickDuration<TickPerSec> GameUpdateTPS;
    milliseconds MsSinceLastDraw;
    milliseconds MsSinceLastUpdate;
    time_point<steady_clock> TimeNow;

public:/*variables*/
    time_point<steady_clock> TimeBegin;
    time_point<steady_clock> PrevUpdateTime;
    time_point<steady_clock> PrevDrawTime;
    time_point<steady_clock> NextUpdateTime;

public:/*constructors*/
    GameTimer();
    ~GameTimer();

public:/*methods*/
    time_point<steady_clock> CurrentTime();
    float UpdateTick();
    float DrawTick();
    std::string PrintTimeSinceLasUpdate();
    std::string PrintTimeSinceLastDraw();

private:/*methods*/
    float Interpolation();
    float RecordTimeSinceLastUpdate();
    float RecordTimeSinceLastDraw();
};

///////////////////////////////////////////////////

template <size_t TickPerSec>
GameTimer<TickPerSec>::GameTimer()
    :GameUpdateTPS{ 1 }, MsSinceLastDraw{ 0 }, MsSinceLastUpdate{ 0 }
{
    TimeBegin = TimeNow = PrevDrawTime = PrevUpdateTime = NextUpdateTime = steady_clock::now();
}

template <size_t TickPerSec>
GameTimer<TickPerSec>::~GameTimer()
{
}

template <size_t TickPerSec>
time_point<steady_clock> GameTimer<TickPerSec>::CurrentTime()
{
    TimeNow = steady_clock::now();
    return TimeNow;
}

template <size_t TickPerSec>
float GameTimer<TickPerSec>::UpdateTick()
{
    NextUpdateTime += GameUpdateTPS;
    return RecordTimeSinceLastUpdate();//this is basically returning DeltaTime
}

template <size_t TickPerSec>
float GameTimer<TickPerSec>::RecordTimeSinceLastUpdate()
{
    MsSinceLastUpdate = duration_cast<milliseconds>(CurrentTime() - PrevUpdateTime);
    PrevUpdateTime = CurrentTime();
    return duration_cast<FloatSeconds>(MsSinceLastUpdate).count();
}

template <size_t TickPerSec>
float GameTimer<TickPerSec>::DrawTick()
{
    RecordTimeSinceLastDraw();//this is recorded to calculate FPS
    return Interpolation();
}

template <size_t TickPerSec>
float GameTimer<TickPerSec>::RecordTimeSinceLastDraw()
{
    MsSinceLastDraw = duration_cast<milliseconds>(CurrentTime() - PrevDrawTime);
    PrevDrawTime = CurrentTime();
    return duration_cast<FloatSeconds>(MsSinceLastDraw).count();
}

template <size_t TickPerSec>
float GameTimer<TickPerSec>::Interpolation()
{
    nanoseconds TimeOffset_ns = CurrentTime() + GameUpdateTPS - NextUpdateTime;
    nanoseconds GameUpdateTPS_ns = duration_cast<nanoseconds>(GameUpdateTPS);
    return (static_cast<float>(TimeOffset_ns.count()) / static_cast<float>(GameUpdateTPS_ns.count()));
}

template <size_t TickPerSec>
std::string GameTimer<TickPerSec>::PrintTimeSinceLasUpdate()
{
    return "| LastUpdate: " + std::to_string(duration_cast<milliseconds>(steady_clock::now() - PrevUpdateTime).count()) + "ms ago|";
}

template <size_t TickPerSec>
std::string GameTimer<TickPerSec>::PrintTimeSinceLastDraw()
{
    return "| LastFrame: " + std::to_string(duration_cast<milliseconds>(steady_clock::now() - PrevDrawTime).count()) + "ms ago|";
}
 

Ci sono discussioni simili a riguardo, dai un'occhiata!

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!

Discussioni Simili