2009-03-15 15 views
2

J'essaye de créer une boucle d'étape fixe dans mon programme, mais pour une raison quelconque je ne peux pas sembler l'obtenir pour fonctionner correctement. Fondamentalement ce dont j'ai besoin est une boucle qui fait:Comment implémenter une boucle à pas fixe?

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

ou quelque chose de similaire, avec un. J'ai essayé d'utiliser Thread.Sleep mais je ne suis pas si sûr que cela m'a donné une vraie boucle d'étape fixe, et quand j'ai essayé d'implémenter la solution sur le site Web this j'ai eu des problèmes en raison du fait que je ne pouvais pas voir pour sauvegarder l'état des objets affectés par la boucle. Cela m'a forcé à exclure cette partie, ce qui a entraîné un ralentissement quand il y avait beaucoup d'objets dans la boucle.

Comment puis-je obtenir une boucle d'étape fixe sans devoir constamment enregistrer l'état de chaque objet de la boucle?

+0

Quel framerate essayez-vous d'atteindre? Cela affecte la meilleure méthode de façon spectaculaire. –

Répondre

1

Si vous travaillez sur un moteur de jeu, une minuterie ne fonctionnera probablement pas bien. Les temporisateurs n'ont pas assez de précision pour gérer une boucle de pas fixe et la garder régulière.

Vous devrez l'implémenter à l'aide d'une minuterie de haute précision. Dans ce cas, vous devrez utiliser the Stopwatch class. Cela vous permettra d'avoir assez de précision pour suivre votre temps écoulé avec précision.

Vous aurez juste besoin de garder une trace de la fréquence à laquelle vous souhaitez mettre à jour (dans Ticks), puis utilisez un chronomètre pour mesurer le temps qu'il faut pour chacune de vos mises à jour. Après cela, vous pouvez potentiellement utiliser un appel Sleep() pour bloquer, mais réaliser que le sommeil n'aura qu'une précision de 14-15 ms sur la plupart des systèmes, même si vous ne dormez que pendant 0 ou 1 ms. Cela peut ralentir trop votre boucle, vous devrez peut-être mettre en place un verrou de spin ou similaire (while (waiting) {do something}). Sleep() est sympa car il économise du CPU si vous le pouvez, le spin lock va manger des cycles CPU pendant que vous attendez, mais vous donne plus de précision.

+0

Oui, une solution illustrée sur un site Web que j'ai trouvé utilisait une minuterie haute résolution, alors j'ai essayé la classe Stopwatch avec sa solution. Le problème était que lorsque mon système de particules ajoutait de nombreuses particules, cela ralentissait la boucle même si son code devait compenser. Je suis toujours désemparé. – Bevin

+0

Si le système de particules prend plus d'une heure, il n'y a aucun moyen de compenser. Vous avez deux options - optimiser vos particules pour être plus rapide, ou réduire votre framerate globalement. Sinon, un cadre en cours d'exécution entraînera un retard. S'il est plus rapide qu'une seule tique, il devrait le gérer. –

+0

Comment gérez-vous la pause entre les images? Appelez-vous Sleep() ou écrire un verrou de spin? –

1

Demandez-vous une boucle qui fonctionnera pratiquement toutes les tiques? Cela ressemble à un Timer. Il y a quelques minuteries dans la BCL. A timer for servers ou un for Window Forms.

Vous pouvez les utiliser selon ces lignes. Ce qui suit est code de pseudo et destiné à montrer généralement comment cela est fait. En d'autres termes, il ne compilera probablement pas si vous copiez et collez. .

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; 
     } 

       } 

} 

Vous pouvez également utiliser une fermeture pour envelopper votre état de la méthode que la minuterie est définie dans

Modifier

J'ai lu l'article et je comprends la question; Cependant, vous pouvez toujours utiliser une minuterie, mais vous devez, comme il l'indique, définir votre fonction pour appeler les moteurs pour chaque intervalle auquel vous avez défini votre moteur physique.

Etes-vous en utilisant WPF, ils ont certains événements qui, je crois, se déclencher à des taux réguliers pour les animations de doign.

Modifier

Je mis à jour mon exemple de code pour vous montrer mon interprétation, mais essentiellement ce que vous faites est de définir un intervalle pour votre moteur physique, et ce que vous devez faire à chaque passage à travers votre « boucle/minuterie tout "détermine le temps réel passé depuis la dernière itération. Vous stockez ensuite ce delta dans un Accumalator, que vous utiliserez pour compter jusqu'à ce que vous ayez appelé votre moteur physique pour tous les intervalles que vous avez manqués depuis votre dernier appel. Ce qui me pose problème est de savoir si l'utilisation d'une minuterie ici est préférable, puis d'avoir un thread dédicacé en sommeil, ou une autre implémentation.

+0

L'utilisation d'une minuterie entraînera des problèmes. Que faire si la méthode de mise à jour prend légèrement plus de temps que la boucle est censée? Comme la minuterie est toujours activée à un certain intervalle, cela augmentera l'intervalle et provoquera un ralentissement. Jetez un oeil à l'article auquel je suis lié. – Bevin

+0

Attendez, comment voulez-vous dire exactement? Je ne comprends pas ce que vous entendez par "appelez les moteurs pour chaque intervalle auquel vous définissez votre moteur physique". – Bevin

+0

Oui, je vois. J'ai essayé d'utiliser ce système avec un accumulateur, mais cela a fait chuter le framerate extrêmement bas; ce qui s'est passé était que la fonction de tirage était assise après l'accumulateur, et elle tirait aussi souvent que possible, mais la fonction de mise à jour n'était pas appelée aussi souvent et entraînait un décalage. – Bevin

0

Thread.Sleep ne va pas vous donner une boucle d'étape fixe, il attendra simplement la durée donnée avant de laisser votre programme continuer. La fréquence réelle dépendra de la précision du sommeil et de la cohérence de votre programme en ce qui concerne l'exécution du même temps d'horloge à chaque étape. Si vous pouvez accommoder une certaine dérive, cependant, cela pourrait être adéquat et est certainement le plus facile.

Pour obtenir une vraie boucle d'étape fixe, ce dont vous avez besoin est un interval timer qui exécute votre code via un rappel lorsque le temporisateur expire. Notez que ce n'est plus vraiment une boucle, mais que le code est exécuté périodiquement, ce qui est, je pense, ce que vous voulez. Encore une fois, il peut y avoir de la dérive et vous devrez peut-être ajuster l'intervalle à chaque étape pour que l'événement suivant se déclenche au bon moment si vous avez besoin de plus de précision. Il n'est pas vraiment possible de le rendre parfaitement précis - même les temporisateurs d'intervalles basés sur le matériel ont une précision, après tout, mais j'espère que vous pouvez le rendre assez précis pour vos besoins.