Controllo velocita' di un game

di il
15 risposte

Controllo velocita' di un game

Ciao ragazzi! Qualche mese fa in una delle mie domande qualcuno ha risposto a una delle mie domande sui videogames, qualcosa tipo "Come fanno i personaggi in un videogame a muoversi con una velocita' costante, senza dipendere dalle prestazioni del pc o dagli FPS?", e se non sbaglio la risposta era "un videogame ha un ciclo particolare".
Ebbene, dato che per scuola/hobby ho intenzione di fare un mini videogioco (piu' articolato di quelli normali di scuola, far muovere una navicella con Allegro e controllare se si viene toccati dalla nave spaziale aliena, e sparare contro di lei per ucciderla, tutto questo senza controlli o algoritmi che usano del vero tempo... Per dare un "delay" tra uno sparo e l'altro incrementiamo una variabile fino a 20.000 >.<) vorrei sapere come effettivamente e' strutturato questo "ciclo del videogioco", in modo da avere una velocita' del gioco universale, senza dipendere dagli fps.
Grazie ancora per tutti i consigli e le dritte che mi date
P.s. su internet non trovo niente, se sapete come si chiama e se e' un argomento abbastanza comune potete anche darmi la chiave di ricerca e continuo io individualmente

15 Risposte

  • Re: Controllo velocita' di un game

    Non conosco la libreria allegro nello specifico ma posso dire, in generale, un videogioco si basa su un ciclo principale che ripete all'infinito queste operazioni, a grandi linee: acquisizione mossa utente, elaborazione strategia macchina, aggiornamento video.
    La velocità di esecuzione dipende dalla macchina e quindi bisogna fare in modo che il ciclo sia sincronizzato, che venga eseguita una passata ogni tot msec.
    La sincronizzazione può avvenire banalmente con una Sleep() ad ogni ciclo oppure con una GetTickCount() opportuna per far partire il ciclo ogni tot msec (ovvero aspettando che siano trascorsi tot msec dal precedente passaggio).
    Ti suggerisco comunque di seguire qualche tutorial per vedere come viene fatto tutto questo: ad esempio qui https://wiki.allegro.cc/index.php?title=Allegro_5_Tutorial/Timers c'è un esempio che utilizza un timer di allegro.
  • Re: Controllo velocita' di un game

    Scusa, mi sono dimenticato di dire che allegro lo uso solo a scuola, a casa uso le sfml >.<
    Il ciclo principale è la base del gioco, ma viene eseguito un diverso numero di volte a seconda della potenza del calcolatore. Non credo che sleep sia il comando giusto, perchè prova a limitare gli FPS, e se il pc non è particolarmente potente e fa meno fps previsti il gioco risulta tempisticamente più lento.
    Su internet ho trovato qualche frase che usava update(), ma non ho trovato altro...
    Nel mentre ho ipotizzato un sistema, che credo funzioni, in poche parole i millisecondi passati tra frame e frame variano a seconda della potenza del calcolatore e la velocitá(ad esempio) viene calcolata dipendendo da quanti mls sono passati da un frame all'altro, ad esempio aggiungendo per ogni millisecondo (sempre tra un frame e l'altro) 0.001 alla posizione del personaggio. In questo modo dopo 1 secondo universale si dovrebbe percorrere 1 unità, no?
    Anyway mi guardo il tutorial che mi hai dato tu.
  • Re: Controllo velocita' di un game

    Mi sa che ti stai complicando inutilmente.
    Vedi i tutorial, in particolare guarda : puoi leggere il tempo che è passato dall'ultima restart() quindi, ad esempio, se vuoi sincronizzarti ogni 200 msec (scrivo in pseudocodice):
    restart();
    while(1)
    {
        if( elapsed() >= 200 )
        {
           restart();  // rilancio timer
           // faccio una passata....
        }
    }
    
  • Re: Controllo velocita' di un game

    Ma cosi' ha lo stesso principio dello sleep...
    Quello che voglio fare io non mi complica la vita, anzi mi sembra molto semplice...
  • Re: Controllo velocita' di un game

    Il trucco sta nel creare una classe che lavora in un thread separato , questa classe che basa principalmente la sua funzionalità sul timer del processore effettua il conteggio dei frame al secondo e attraverso un meccanismo di sincronizzazione dei dati tra vari thread conduce all'esecuzione ("fanno da chiave") di critical section.
    In questo modo puoi avvere un controllo completo dei meccanismi di rendering- input e di particolari spezzoni di codice.

    Cominque ti consiglio di studiare :
    -Mutex
    -Spinlock
    -Deadlock
    -Critical section

    Inoltre approfondire le librerie :
    -thread
    -mutex
    -future
    -chrono

    Inoltre visita questo sito:
    (Anche se gestisce le cose in un modo differente dal mio rimane un ottimo esempio)
  • Re: Controllo velocita' di un game

    Grazie mille a entrambi
    Tutti i videogame (o almeno la maggior parte) usano questo sistema?

    Avevo fatto una procedura per gli fps:
    
    void FramePerSeconds(string fontName, int xpos, int ypos, int& fps, int& curFps) //The route where you need to load the font, the position of the fps, the unstable fps and the current fps (refreshed every seconds)
    {
        /****Variables****/
        int mls, fps_mls;
        static Font font;
        static double pastTimeFps;
        static double pastTime;
        static Text fpsText;
        static char load = 'n';
        stringstream converter;
        string fpsString;
        if (load == 'n')  //If fpsText and font is not loaded yet
        {
            fpsText.setFont(font);
            fpsText.setCharacterSize(20);
            fpsText.setColor(Color::Green);
            fpsText.setPosition(xpos, ypos);
            fpsText.setStyle(Text::Bold);
            /**End Variables**/
            if (!font.loadFromFile(fontName))
            {
                cout <<"Error, cannot open " <<fontName <<endl;
    
            }
            load = 'y'; //fpsText and font is loaded
        }
    
        converter <<curFps;
        fpsString = converter.str();
        fpsText.setString(fpsString);
    
        fps_mls = starterClock - pastTimeFps;
        mls = starterClock - pastTime;
        if (mls >= 1)
        {
            pastTime = starterClock;
            fps = fps + 1;
            if (fps_mls >= 1000)
            {
                pastTimeFps = starterClock;
                curFps = fps;
                fps = 0;
            }
        }
        window.draw(fpsText);
    }
    E' scritta in modo orribile, ma almeno funziona
  • Re: Controllo velocita' di un game

    Quello che voglio fare io non mi complica la vita, anzi mi sembra molto semplice...
    Il punto è che se utilizzi una libreria come allegro ed sfml puoi utilizzare gli strumenti che ti vengono offerti, senza dover reinventare la ruota (e se anche non avevi la libreria sfml puoi utilizzare GetTickCount() delle API Win32, supponendo che tu lavori in Windows).
    Se devi aggiornare a video il movimento di un qualche oggetto non c'è bisogno che lo sposti ad altissime frequenze, tanto per l'occhio umano non cambia niente: credo che aggiornare la situazione a video ogni 10 msec (100 Hz) sia molto più che sufficiente. Ciò vuol dire che ogni 10 msec puoi aggiornare lo stato del gioco e quindi fai che ogni passata aspetti che siano trascorsi 10 msec.
    Guarda bene l'esempio qui https://wiki.allegro.cc/index.php?title=Allegro_5_Tutorial/Timers dove è impostato un refresh di 60 FPS... poi se vuoi complicarti inutilmente la vita fallo pure
    Buono studio e buon divertimento!
  • Re: Controllo velocita' di un game

    HLa maggior parte, anche perchè sleep() può portare una perdita di prestazioni , in effetti risulta molto meglio switcare tra i vari thread con yield(senza esagerare).. Diciamo che ci vuole un po di esperienza ,conoscere come funzionano i s.o. e conoscenza dell'assembly per avere un quadro completo della situazione .

    Visto che oggi sono di buon umore e ho avuto 10 minuti liberi ho pensato per rendeti il quadro della situazione meno complicato e un po' più pratico scrivendo una piccola porzione di codice che potrebbe rivelarsi utile:
    Ovviamente non garantisco che non ci possano essere errori la porzione di codice ha il solo scopo di rendere il meccanismo più chiaro
    
    include <chrono>
    #include <thread>
    #include <mutex>
    #include <future>
    //tipi vari
    enum Status_t : std::uint8_t //enumeratore che gestisce l'esecuzione // fallo gestire al main 
    {
    	Runing,
    	Closing,
    	Pause,
    	Reset,
    	In_Exception
    };
    
    typedef unsigned int FPS_t;
    
    
    //Variabili gestite dal main o altri thread
    Status_t Status; // gestisce lo stato del programma;
    FPS_t * FPS;
    
    
    // queste funzioni poi implementare nello stesso thread del Timer oppure (Meglio) farle gestire i altri thread
    /* 1 */	void _stdcall RenderScreen(unsigned char * Semaphore /* passa il puntatore della variabile semaphore della classe Timer */) 
    {/* guarda come gestisco il codice nella terza funzione*/};
    /* 2 */	void _stdcall PrintFPS(FPS_t &FPS) 
    {/*inserisci il tuo codice */};
    /* 3 */ void _stdcall Elabora_Input(unsigned char * Semaphore /* passa il puntatore della variabile semaphore della classe Timer */ ) 
    { // in un thread separato
    	while (Status != Status_t::Closing)
    	{
    		while (Status == Status_t::Pause)
    		{
    			std::this_thread::yield();
    		}
    		while (Status == Status_t::Runing &&(( *Semaphore bitand 00000001b ) == 1))
    		{
    			thread_local register auto K = std::getchar();
    				switch(K){/* inserisci il codisce */}
    				// il primo bit ritorna a zero dopo aver elaborato l'input
    				*Semaphore == *Semaphore xor 00000001b;
    		}
    
    		std::this_thread::yield();
    	}
    };
    
    
    
    
    
    class Timer
    {
    	std::thread * Thread;
    	
    
    public:
    	Timer();
    	~Timer();
    	
    	void StartTimerThread(void) noexcept;
    	unsigned char * Semaphore; // ? unicamente gestito dal thread che generi dalla classe 
    							   // una cattiva implementazione pottrebe causare "race conditions"
    			// usa unsigned char/short/int al posto dei bool e impara a operare su bit se non lo sai fare;
    			////////////////////////////////////////////////////////
    			// Esempio 
    			// bit. N	1			2			3			4			5			6			7			8
    			//			Input		
    			//			Abilitato	
    private:
    	
    
    };
    
    Timer::Timer()
    {
    	this->Thread = nullptr;
    }
    
    Timer::~Timer()
    {
    	while (!this->Thread->joinable()) {/* SpinLock ;) */ std::this_thread::yield(); };
    	this->Thread->join();
    	delete this->Thread;
    	delete this->Semaphore;
    	
    }
    
    
    
    void Timer::StartTimerThread(){
    this->Thread = new std::thread([this]() {
    	const std::chrono::duration<std::chrono::milliseconds> s = 1000;
    	const std::chrono::duration<std::chrono::milliseconds> ds = 100;
    	std::chrono::time_point <std::chrono::milliseconds> Point_B;
    	std::chrono::time_point <std::chrono::milliseconds> Point_C;
    	std::chrono::time_point <std::chrono::milliseconds> Point_A = std::chrono::system_clock::now();
    
    	while (Status != Status_t::Closing)
    	{
    		std::chrono::time_point <std::chrono::milliseconds> Point_B = std::chrono::system_clock::now();
    
    		if (Point_C + ds < Point_B)
    		{
    			Point_C = Point_B + ds;
    			*(this->Semaphore) = *(this->Semaphore) bitor 00000001b;
    			// ogni 0,1 sec imposta a 1 il 1? bit di semaphore abilitando la criticalsection della funzione elabora_input();
    		}
    
    		if (Point_A + s < Point_B)
    		{
    			Point_C = Point_B;
    			Point_A = Point_B;
    			FPS_t * Ptr = FPS;
    			FPS = new FPS_t;
    			PrintFPS(Ptr);
    			delete Ptr;
    
    		}
    	}
    
    	std::this_thread::yield();
    });
    }
    
  • Re: Controllo velocita' di un game

    Sleep() non porta a nessuna perdita di prestazione!
  • Re: Controllo velocita' di un game

    Da quello che so io , Sleep() l'unico "vantaggio" che può dare è "far addormentare" il thread in modo che il programma metta meno carico nella cpu, il che può servire in programmini che offrono servizi di sincronizzazione in modo da avere il minor impatto possibile(la cosa peggiore è che alcune funzioni sleep() ormai obsolete non danno nemmeno questo vantaggio perché obbliga il thread a eseguire un ciclo eterno fino a quando non si supera il periodo di pausa).

    Per spremere al massimo la cpu invece si dovrebbe creare un algoritmo simile a quello, si avrà un carico di cpu al 40-50% al posto del 20%(cifre a caso) ma son sicuro che ti da un vantaggio notevole per quanto riguarda la fisica e la grafica.
  • Re: Controllo velocita' di un game

    Nogar, non inventarti soluzioni fantasione e strampalate!
    Un gioco non e' altro che un software di simulazione.

    L'approccio che si attua in questi casi e' basato sul seguente principio:

    t0: inizializzazione dello stato degli oggetti del sistema
    t1: aggiornamento dello stato degli oggetti del sistema sapendo che e' passato (t1-t0) millisecondi.
    ...
    tn: aggiornamento dello stato degli oggetti del sistema sapendo che e' passato (tn-t[n-1]) millisecondi.

    Questo se hai un thread unico.

    In alternativa, usi un timer che invia un TICK a TUTTI gli oggetti del sistema ogni N millisecondi.

    Ogni oggetto tiene traccia del timestamp dell'ultimo aggiornamento del suo stato, e, al termine delle elaborazioni attivate da un TICK, SI METTE IN ATTESA DEL PROSSIMO TICK.
    Quello che ci si deve assicurare e' che se arriva UN'ALTRO TICK mentre e' in esecuzione l'aggiornamento di un TICK precedente, LO DEVE SCARTARE.

    Con questo paradigma (che e' un paradigma di programmazione AD EVENTI) a te non interessa minimamente le temporizzazioni o la velocita' della CPU, perche' ci si basa sulla quantita di tempo passata tra un'elaborazione e l'altra, cioe' un'informazione ASSOLUTAMENTE LOCALE.

    Se hai un computer veloce, potrai avere un maggior numero di aggiornamenti. Se ne hai uno lento, il numero di aggironamenti sara' inferiore, ma comunque il tuo giochino continuera' a funzionare correttamente.

    Come noti, qui la sleep NON VIENE MINIMAMENTE PRESA IN CONSIDERAZIONE.

    E soprattutto evita di fare considerazioni sulla programmazione concorrente se non hai chiaro come funziona.

    Ad esempio, molto spesso uno sleep(0) e' equivalente ad uno yield() e comunque lo sleep forza gia' di suo il contest switch.
  • Re: Controllo velocita' di un game

    La soluzione con 1 solo thread era esattamente come avevo immaginato!
    Il resto è un pochino troppo complicato e devo studiarmi tutto il multithreading.
  • Re: Controllo velocita' di un game

    @Nogar : Grazie mille per il codice, lo studierò attentamente ;D
  • Re: Controllo velocita' di un game

    Volevo solo aggiungere due note sul mio pensiero
    @Devid1910: guarda i link che ti ho postato, soprattutto quello dal tutorial di Allegro; parti da cose semplici come un aggiornamento del gioco e video a 60 Hz come proposto nel tutorial: se poi vuoi andare più veloce farai delle ottimizzazioni ma... che senso ha ricalcolare la situazione del gioco in maniera frenetica se comunque il tuo occhio non potrà vedere tutto questo? Il mio suggerimento è quello di utilizzare gli strumenti che ti fornisce la libreria.
    @Nogar: non discuto sulle ottimizzazioni che proponi ma, appunto, si tratta di ottimizzazioni! Il nostro amico Devid1910 sta imparando e aveva bisogno di uno "schema" da seguire perchè il suo dubbio è proprio su come strutturare il software. Sarebbe interessante se tu potessi proporre delle letture, libri di testo meglio che tutorial, che possano guidare il nostro amico nella progettazione del suo videogioco.
Devi accedere o registrarti per scrivere nel forum
15 risposte