Domanda

Sto tentando di creare un loop a passi fissi nel mio programma, ma per qualche ragione non riesco proprio a farlo funzionare correttamente. Fondamentalmente quello di cui ho bisogno è un ciclo che faccia:

while(!over) {
    Update(elapsedtime);
    Draw(elapsedtime);
}

o qualcosa di simile, con a. Ho provato a usare Thread.Sleep ma non sono così sicuro che mi abbia dato un vero e proprio ciclo a passo fisso e quando ho provato a implementare la soluzione su questo sito web Ho avuto problemi a causa del fatto che non riuscivo a vedere un modo per salvare lo stato degli oggetti interessati dal loop. Questo mi ha costretto ad escludere quella parte, che ha portato al rallentamento quando c'erano molti oggetti nel loop.

Come posso ottenere un loop a passo fisso senza dover costantemente salvare lo stato di ogni oggetto nel loop?

È stato utile?

Soluzione

Se stai lavorando su un motore di gioco, probabilmente un timer non funzionerà bene. I timer non hanno una precisione sufficiente per gestire un loop a step fisso e mantenerlo regolare.

Dovrai implementarlo usando un timer ad alta precisione. In questo caso, ti consigliamo di utilizzare la classe Cronometro. Questo ti permetterà di avere abbastanza precisione per tracciare accuratamente il tuo tempo trascorso.

Devi solo tenere traccia della frequenza con cui vuoi aggiornare (in tick), quindi utilizzare un cronometro per misurare il tempo necessario per ciascuno degli aggiornamenti. Successivamente, puoi potenzialmente utilizzare una chiamata Sleep () per bloccare, ma tieni presente che la sospensione avrà solo una precisione di 14-15 ms sulla maggior parte dei sistemi, anche se dormi solo per 0 o 1 ms. Questo potrebbe rallentare troppo il tuo loop, quindi potresti aver bisogno di implementare uno spin lock o simili (mentre (in attesa) {do qualcosa}). Sleep () è utile perché consente di risparmiare CPU, se possibile, il blocco dello spin consumerà i cicli della CPU mentre aspetti, ma ti dà più precisione.

Altri suggerimenti

Stai chiedendo un ciclo che verrà eseguito praticamente ogni n tick? Sembra un Timer . Ci sono un paio di timer nel BCL. Un timer per server o uno per Window Forms .

Puoi usarli lungo queste linee. Quello che segue è il codice psuedo e intende mostrare in generale come viene fatto. In altre parole, probabilmente non verrà compilato se si copia e incolla.

public class RepeatingTask
{
    public MyObjectState _objectState;

    public RepeatingTask(Timespan interval)
    {
       Timer timer=new Timer(Timer_Tick); //This assumes we pass a delegate. Each timer is different. Refer to MSDN for proper usage
       timer.Interval=interval;
       timer.Start();

    }
    private DateTime _lastFire;
    private void Timer_Tick()
    {
       DateTime curTime=DateTime.Now();
       DateTime timeSinceLastFire = curTime-lastFireTime;
       _lastFire=DateTime.Now(); //Store when we last fired...

       accumulatedtime+=timeSinceLastFire
       while(accumulatedtime>=physicsInterval)
       {
          Update(physicsInterval);
          accumulated-=physicInterval;
       }

               }

}

Puoi anche usare una chiusura per concludere il tuo stato del metodo in cui è definito il timer.

Modifica

Ho letto l'articolo e ho compreso il problema; tuttavia, è ancora possibile utilizzare un timer, ma è necessario, poiché indica di impostare la funzione per chiamare i motori per ogni intervallo su cui si imposta il motore fisico.

Stai usando WPF hanno alcuni eventi che credo vengano lanciati a tassi costanti per le animazioni Doign.

Modifica

Ho aggiornato il mio esempio di codice per mostrarti la mia interpretazione, ma fondamentalmente quello che fai è definire un intervallo per il tuo motore fisico, e quindi cosa devi fare ad ogni passaggio attraverso il tuo "Loop / Timer" qualunque cosa " è determinare quanto tempo reale è trascorso dall'ultima iterazione. Quindi memorizzi quel delta in un accumulatore, che userai per il conto alla rovescia fino a quando non avrai chiamato il tuo motore fisico per tutti gli intervalli che hai perso dall'ultima volta che hai chiamato.

Quello con cui ho difficoltà è se usare un timer qui è meglio, quindi avere un thread dedidicato inattivo o qualche altra implementazione.

Thread.Sleep non ti darà un ciclo a passi fissi, ma semplicemente attenderà il tempo prestabilito prima di consentire al programma di continuare. La frequenza effettiva dipenderà dalla precisione del sonno e dalla coerenza del programma nell'esecuzione della stessa quantità di tempo dell'orologio a parete ad ogni passaggio. Se riesci a compensare qualche deriva, questo potrebbe essere adeguato ed è certamente il più semplice.

Per ottenere un vero e proprio ciclo fisso, ciò di cui hai bisogno è un timer intervallo che esegue il codice tramite una richiamata allo scadere del timer. Nota che in realtà non è più un ciclo, ma il tuo codice viene eseguito periodicamente, che è, penso, quello che vuoi. Ancora una volta, potrebbe esserci qualche deriva e quindi potrebbe essere necessario regolare l'intervallo ad ogni passaggio in modo che l'evento successivo si attivi al momento giusto se è necessaria una maggiore precisione. Non è davvero possibile renderlo perfettamente accurato - anche i timer di intervallo basati su hardware hanno una precisione, dopo tutto - ma spero che tu possa renderlo abbastanza accurato per i tuoi scopi.

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