C'è del male a far controllare incontrollato il ciclo principale del gioco?


19

Mi chiedevo se ci fosse qualche danno possibile quando il mio ciclo di gioco scorre veloce quanto il sistema lo consente?

Al momento ho un loop che, misurando il tempo trascorso in nanosecondi, esegue la logica di gioco e la logica di rendering a velocità predefinite senza problemi. In effetti, ogni logica che faccio nel loop è sincronizzata su una certa quantità di chiamate al secondo.

Il loop stesso, però, corre più veloce di quello che gli piace e arriva a circa 11,7 milioni di loop al secondo sulla mia macchina.

Loop (semplice pseudocodice):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

La mia domanda è fondamentalmente se quel semplice loop, se non funziona a una velocità controllata, può danneggiare un sistema?

Modifica: ciò significa che la mia logica viene eseguita 30 volte al secondo (30 tps), il mio renderer funziona a 60 fps, eseguo il polling degli input 100 volte al secondo e c'è anche qualche logica per far fronte alla logica o al rendering impiegando più tempo del previsto . Ma il loop stesso non è limitato.

Modifica: usando Thread.sleep()ad es. L'acceleratore del loop principale fino a 250 loop al secondo porta a una riduzione, ma i loop funzionano a circa 570 loop al secondo invece dei 250 desiderati (aggiungerà il codice quando sono sul mio computer desktop ..)

Modifica: Eccoci, un gameloop java funzionante per chiarire le cose. Inoltre, sentiti libero di usarlo ma non rivendicalo;)

private void gameLoop() {

    // Time that must elapse before a new run
    double timePerPoll = 1000000000l / targetPPS;
    double timePerTick = 1000000000l / targetTPS;
    double timePerFrame = 1000000000l / targetFPS;
    int maxFrameSkip = (int) ( (1000000000l / MINIMUM_FPS) / timePerTick);

    int achievedPPS = 0;
    int achievedFPS = 0;
    int achievedTPS = 0;

    long timer = TimeUtils.getMillis();

    int loops = 0;

    int achievedLoops = 0;

    long currTime = 0l;
    long loopTime = 0l;

    long accumulatorPPS = 0l;
    long accumulatorTPS = 0l;
    long accumulatorFPS = 0l;

    long lastTime = TimeUtils.getNano();

    while(!isRequestedToStop) {
        currTime = TimeUtils.getNano();
        loopTime = currTime - lastTime;
        lastTime = currTime;

        loops = 0;

        accumulatorPPS += loopTime;
        accumulatorTPS += loopTime;
        accumulatorFPS += loopTime;

        if(accumulatorPPS >= timePerPoll) {
            pollInputs();
            playerLogic();
            achievedPPS++;
            accumulatorPPS -= timePerPoll;
        }

        while(accumulatorTPS >= timePerTick && loops < maxFrameSkip) {
            tick();
            achievedTPS++;
            accumulatorTPS -= timePerTick;
            loops++;
        }

        // Max 1 render per loop so player movement stays fluent
        if(accumulatorFPS >= timePerFrame) {
            render();
            achievedFPS++;
            accumulatorFPS -= timePerFrame;
        }

        if(TimeUtils.getDeltaMillis(timer) > 1000) {
            timer += 1000;
            logger.debug(achievedTPS + " TPS, " + achievedFPS + " FPS, "
                    + achievedPPS + " Polls, " + achievedLoops + " Loops");
            achievedTPS = 0;
            achievedFPS = 0;
            achievedLoops = 0;
        }

        achievedLoops++;
    }
}

Come puoi vedere non c'è quasi nessun codice eseguito su ogni loop ma sempre una certa selezione in base a quanto tempo reale è passato. La domanda si riferisce a quel "ciclo di lavoro" e al modo in cui influenza il sistema.


2
link obbligatorio "correggi il tuo timestep": gafferongames.com/game-physics/fix-your-timestep, per favore leggilo. Si noti inoltre che Thread.Sleep () non può essere utilizzato per limitare il timestep in modo affidabile poiché il sistema operativo non garantisce di restituire il controllo all'applicazione dopo il periodo di tempo richiesto. (Può variare).
Roy T.

Sì, questo è il problema con lo pseudocodice. Ho fatto la maggior parte di ciò di cui gaffer scrive (attualmente sto lavorando su un'implementazione delta che integra la mia struttura ad anello). Se non riesco a utilizzare Thread.sleep (), cos'altro è disponibile in Java standard?
dot_Sp0T

1
Di solito usi un loop occupato con Thread.Sleep (0) e speri per il meglio. Gaffer ne parla molto in alcuni articoli.
Roy T.

Bene, questo è fondamentalmente quello che ho fatto, solo senza il thread.sleep (0), E ciò che originariamente ha provocato questa domanda. C'è qualche danno in un loop occupato (o non occupato come anche quella piccola logica ripetitiva viene ridotta) per il sistema?
dot_Sp0T

Qualche tempo fa, un bug ha causato Starcraft II a farlo in determinate condizioni. Ha finito per fondere letteralmente alcune GPU. Quindi sì, è molto probabile che un loop di gioco incontrollato possa causare danni.
Mason Wheeler,

Risposte:


35

Farà funzionare sempre un core della CPU al 100%. Questo di solito non causa alcun danno al sistema. Le CPU sono progettate per funzionare al 100% per ore. Ma su un dispositivo mobile scaricherà rapidamente la batteria e riscalderà il dispositivo, il che probabilmente ti costerà circa una stella nelle valutazioni del tuo negozio. Su un computer desktop questo è meno un problema, ma consumerà più elettricità dell'utente, farà girare più velocemente la ventola della CPU, il che potrebbe causare rumore e sprecare i cicli della CPU che potrebbero altrimenti essere utilizzati da altri processi. Sebbene questi non siano difetti critici, sono comunque di cattivo stile, quindi dovresti evitarli quando possibile.

Non hai detto nulla su come funziona il tuo loop di logica di gioco internamente, ma quando stai usando l'approccio delta-time (ogni calcolo che fai prende il tempo dall'ultima chiamata in considerazione), hai a che fare con delta- valori temporali. Ciò significa che è possibile riscontrare problemi con inesattezze in virgola mobile che possono causare tutti i tipi di comportamenti strani. Inoltre, la risoluzione del timer di sistema è spesso limitata, quindi quando il tuo loop logico è troppo veloce, puoi ottenere un valore delta-t pari a zero, che quindi potrebbe causare una divisione per zero con conseguente arresto anomalo.

Per mitigare questo problema, dovresti limitare il tuo framerate (grafico e logico) al massimo di ciò che l'occhio umano può percepire. Quanto viene contestato e dipende dal tipo di animazione e dal tipo di display mostrato, ma le stime vanno da 40 FPS a 120 FPS. Ciò significa che è necessario impostare un tempo minimo di esecuzione di ciascun loop tra 20 ms e 8 ms. Se un'iterazione del ciclo termina più velocemente, lasciare che il thread cpu sleepper il periodo rimanente.


Grazie per la spiegazione Philipp. In realtà ho fatto riferimento al fatto che sto correndo a un certo numero di fps e tps e faccio il polling solo una certa quantità di volte al secondo. Cercherò di renderlo più chiaro però.
dot_Sp0T

@ dot_Sp0T Dopo la modifica della domanda, solo il primo paragrafo e l'ultima frase sono pertinenti al tuo caso. Tuttavia, vorrei conservare il secondo e il terzo paragrafo della mia risposta perché potrebbero essere utili per gli altri.
Philipp,

Presumibilmente accetterò anche la tua risposta poiché il primo paragrafo fornisce una risposta perfettamente. Ti dispiacerebbe in qualche modo distinguerlo visivamente dal resto della risposta?
dot_Sp0T

Gioca a Civilization 2 senza patch su un desktop e scoprirai che è un problema. Almeno lo faccio. scrollata di spalle
corsiKa

1
Oltre alle considerazioni sulla batteria e sulla ventola, un maggiore utilizzo della CPU può portare a un eccesso di calore che può essere scomodo (ad esempio laptop, esp il mio rMBP) e persino portare allo spegnimento del PC (specialmente i PC più vecchi con dispositivi di raffreddamento polverosi). Questi fattori sono esasperati se esegui tutti i core al 100%.
NPSF3000,

2

Stai sprecando cicli della CPU. Ciò significa una durata della batteria inferiore su notebook, tablet e telefoni, bollette elettriche più elevate, più calore generato dalla macchina, ventole più rumorose. Inoltre, è possibile che si verifichino cicli da altri importanti processi di sistema (ad esempio il server di finestre potrebbe diventare a scatti), che potrebbe quindi influire sul gameplay. Alcuni programmatori di sistema sui sistemi multitasking preventivo di oggi penalizzano anche le applicazioni che utilizzano troppi cicli, quindi potresti essere veloce per primo, quindi improvvisamente vedere uno strano sussulto quando sei strozzato dal sistema.

Molti giocatori hardcore creano anche PC personalizzati da zero che sono ai margini delle specifiche dei loro fan, nel qual caso una giornata calda e il calore generato dal gioco possono causare l'attivazione delle sicurezze nella macchina (nella migliore delle ipotesi) o persino il surriscaldamento delle parti e morire (nel peggiore dei casi). Quindi, se questo è il tuo pubblico di destinazione, potresti voler assicurarti di lasciare sempre un po 'di spazio "in alto".

Infine, ci sono problemi di gioco in cui potresti avvantaggiare i giocatori con macchine più veloci rispetto a quelli con quelli più lenti. Avere il loop di gioco limitato a una certa frequenza e utilizzare cicli aggiuntivi esclusivamente per aspetti non rilevanti per il gameplay (come il rendering di grafica ad alta fedeltà, effetti sonori surround o altro) renderà il tuo gioco più equo.


Direi al contrario che le persone che costruiscono appassionatamente le loro macchine usano buoni fan, persino il raffreddamento ad acqua. Solo i casi HTPC o mini ITX possono avere limitazioni su quella parte. Altrimenti, le persone povere ma entusiaste useranno il box ventirad. che è abbastanza anche al 100%, caldo ma ok.
v.

Sto solo ripetendo per esperienza. Se guardi Star Trek Online, in realtà hanno un'impostazione separata nella loro GUI che inserirà sleep () nel loro ciclo principale per impedire il surriscaldamento di alcune macchine, quindi non può essere una cosa insolita se il produttore di Neverwinter e STO ha visto la necessità di aggiungerlo. Ricorda che passione! = Abilità. Ci sono molti armeggi esperti e appassionati, ma ci sono anche principianti e altri che stanno ancora imparando, e con un discreto tasso di abbandono, le statistiche dicono che sono la maggioranza.
uliwitness

Grazie a questi commenti sono venuto a rileggere la tua risposta. Elenchi punti validi ma purtroppo non affronti davvero la questione con le controversie sulle macchine più veloci e sulle macchine più lente . Il loop logico (chiamato gameloop nel tuo terzo paragrafo) è limitato. La domanda riguardava il limite del loop backbone che semplicemente rivaluta ogni esecuzione se l'input deve essere tirato, la logica deve essere calcolata o un frame dovrebbe essere reso - quindi nessuna logica più veloce a meno che la tua macchina non sia terribilmente zoppa
dot_Sp0T

1

Non dovresti avere movimenti / gameplay nervosi

Esistono due modi in cui è possibile implementare la logica di gioco: legata a un tempo reale o legata al numero di "turni" / fasi di elaborazione. Se qualche cosa nel tuo gioco si sta muovendo verso sinistra, il movimento sarà diverso se il tuo stepLogic () è stato chiamato 100 anziché 50 volte?

Se il tuo codice tratta esplicitamente il tempo trascorso ovunque e lo fa correttamente, allora potrebbe andare bene; ma se c'è qualcosa nel tuo codice che dipende dal numero di "passaggi", otterrai effetti collaterali indesiderati.

Innanzitutto, la velocità delle cose che dovrebbero muoversi costantemente varierà in modo imprevedibile (a seconda dell'utilizzo del processore), e questo rende i controlli molto fastidiosi: non è possibile eseguire un pugno preciso o un salto se improvvisamente il gioco accelera o rallenta. Anche per i giochi senza "contrazioni", sembra fastidioso e nervoso se le cose non si muovono senza intoppi.

In secondo luogo, potresti avere problemi con le abilità dei personaggi a seconda della velocità del computer - un classico esempio è il vecchio problema di Quake in cui il raggio di salto massimo dipendeva involontariamente dai tuoi fps.

Questi problemi possono apparire anche se si tenta di avere il codice indipendente da fps, l'accumulo di errori di arrotondamento può spesso causare tali bug.


1
Per quanto mi piaccia la tua risposta non è pertinente alla domanda, poiché non sto chiedendo del movimento / gameplay nervoso ma del danno che un loop incontrollato (che non controlla alcuna logica o rendering o qualsiasi altra cosa) può fare al sistema
dot_Sp0T

1

L'unico potenziale danno è il consumo di energia, quindi non farlo sui dispositivi mobili. Al contrario, molti sistemi embedded trascorrono la loro intera vita in un ciclo in attesa che le cose accadano.

Un tempo era del tutto normale il semplice rendering dei frame di gioco il più velocemente possibile, a volte senza nemmeno un timer per compensare il gameplay per diverse velocità della CPU. Il gioco di corse di Bullfrog Hi-Octane ne è stato particolarmente vittima, e sospetto che questo sia ciò a cui si riferisce il poster che menziona Civ2.

Ti consiglio di effettuare almeno il polling degli input su ogni passaggio se utilizzi un sistema Windows o simile, per garantire una risposta degli input rapida.


Non è una questione di timer, è una questione di un cronometro, direi piuttosto, o un contatore delle prestazioni. Uno ha bisogno di ottenere il tempo reale ad un certo punto e un ciclo libero funziona perfettamente. Tuttavia, un timer (nel senso di interruzione regolare) sta tentando di regolare la velocità del loop nell'uso generale. Non lo considero buono. È preferibile utilizzare la funzione swap/ present/ flipfare l'attesa sul segnale vsync, per regolare il loop di gioco.
v.
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.