HTML5 de Toile de boucle de jeu delta calculs de temps
Je suis nouveau sur le développement de jeux. Actuellement, je suis en train de faire un jeu pour js13kgames concours, de sorte que le jeu doit être petit et c'est pourquoi je n'utilise pas de moderne populaire cadres.
Tout en développant mon jeu infini de la boucle j'ai trouvé plusieurs articles et conseils pour la mettre en œuvre. Maintenant, il ressemble à ceci:
self.gameLoop = function () {
self.dt = 0;
var now;
var lastTime = timestamp();
var fpsmeter = new FPSMeter({decimals: 0, graph: true, theme: 'dark', left: '5px'});
function frame () {
fpsmeter.tickStart();
now = window.performance.now();
//first variant - delta is increasing..
self.dt = self.dt + Math.min(1, (now-lastTime)/1000);
//second variant - delta is stable..
self.dt = (now - lastTime)/16;
self.dt = (self.dt > 10) ? 10 : self.dt;
self.clearRect();
self.createWeapons();
self.createTargets();
self.update('weapons');
self.render('weapons');
self.update('targets');
self.render('targets');
self.ticks++;
lastTime = now;
fpsmeter.tick();
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
};
Donc, le problème est dans self.dt
j'ai finalement découvert que la première variante n'est pas adapté à mon jeu, car il augmente toujours et la vitesse d'armes est en augmentation avec elle (par ex. this.position.x += (Math.cos(this.angle) * this.speed) * self.dt;
..
Deuxième variante semble plus approprié, mais correspond-il à ce genre de boucle (http://codeincomplete.com/posts/2013/12/4/javascript_game_foundations_the_game_loop/)?
OriginalL'auteur Kosmetika | 2014-09-01
Vous devez vous connecter pour publier un commentaire.
Une excellente solution pour votre moteur de jeu serait de penser dans les objets et les entités. Vous pouvez penser à tout le monde dans votre monde que des objets et des entités. Ensuite, vous voulez faire un jeu de gestionnaire d'objets qui ont une liste de tous les objets de votre jeu. Ensuite, vous voulez faire une méthode de communication commune dans le moteur si les objets du jeu peuvent faire les déclencheurs d'événement. Les entités de votre jeu, par exemple, un joueur n'aura pas besoin inhérent à partir de n'importe quoi pour obtenir la capacité de rendre à l'écran ou ont la détection de collision. Vous serait simple de rendre les méthodes communes de l'entité que le moteur de jeu est à la recherche pour. Puis, laissez le moteur de jeu de la poignée de l'entité qu'il le voudrait. Les entités de votre jeu peut être créée ou détruite à tout moment dans le jeu, donc vous ne devriez pas coder en dur toutes les entités à tous dans la boucle de jeu.
Vous voulez d'autres objets dans votre moteur de jeu pour répondre à un événement déclenche le moteur a reçu. Cela peut être fait en utilisant des méthodes de l'entité que le moteur de jeu va vérifier pour voir si la méthode est disponible et si c'est passerais les événements de l'entité. Ne pas coder en dur l'un de vos jeu de logique dans le moteur, il bousille la portabilité et limite votre capacité à développer sur le jeu plus tard.
Le problème avec votre code est d'abord de votre appel différents objets de rendu et des mises à jour pas dans le bon ordre. Vous devez appeler TOUTES vos mises à jour alors appel TOUS vos rendus dans cet ordre. Un autre est votre méthode de codage en dur des objets dans la boucle va vous donner beaucoup de problèmes, lorsque vous voulez que l'un des objets de ne plus être dans le jeu ou si vous voulez ajouter plus d'objets dans le jeu plus tard.
Les objets de votre jeu aura un
update()
et unrender()
votre moteur de jeu va regarder pour que la fonction de l'objet/de l'entité et de l'appeler à chaque image. Vous pouvez obtenir de très de fantaisie et de faire le travail du moteur de manière à vérifier si le jeu de l'objet/entité a les fonctions avant de les appeler. par exemple, vous voulez peut-être un objet qui a uneupdate()
mais ne restitue rien à l'écran. Vous pourriez faire le jeu de fonctions de l'objet en option en faisant le moteur vérifier avant de faire appel à eux. C'est aussi une bonne pratique d'avoir uninit()
fonction pour tous les objets du jeu. Lorsque le moteur du jeu démarre la scène et crée les objets, il va commencer par appeler les objets du jeuinit()
lors de la première création de l'objet alors tous les cadres d'appelupdate()
de cette façon, vous pouvez avoir une fonction qui ne s'exécute qu'une fois sur la création et un autre qui s'exécute à chaque image.delta du temps n'est pas vraiment nécessaire que
window.requestAnimationFrame(frame);
vous donnera ~60fps. Donc, si vous êtes de conserver une trace du nombre d'images que vous pouvez dire combien de temps a passé. Les différents objets dans le jeu peuvent ensuite (à partir d'un point défini dans le jeu et que le nombre d'images a) déterminer combien de temps sa fait quelque chose en fonction de sa nouvelle nombre d'images.J'ai créé un jeu complet du moteur situé à https://github.com/Patrick-W-McMahon/Jinx-Engine/tree/Dev si vous examinez le code à https://github.com/Patrick-W-McMahon/Jinx-Engine/blob/Dev/JinxEngine.js vous verrez un entièrement fonctionnel du moteur de jeu construit à 100% en javascript. Il comprend des gestionnaires d'événements et permet les appels entre des objets qui sont passés dans le moteur à l'aide de l'événement de la pile des appels. découvrez quelques exemples https://github.com/Patrick-W-McMahon/Jinx-Engine/tree/Dev/examples où vous verrez comment il fonctionne. Le moteur peut tourner autour de 100 000 objets de toutes rendues et exécutées par image à un taux de 60 images par seconde. Cela a été testé sur un core i5. différents matériels peuvent varier. la souris et le clavier événements sont intégrés dans le moteur. objets passés dans le moteur juste besoin d'écouter l'événement transmis par le moteur. La gestion des lieux et multi scène de soutien est en cours de construction pour les jeux plus complexes. Le moteur prend également en charge la haute densité de pixels des écrans.
Relu mon code source devrait vous mettre sur la piste pour la construction d'un plus fonctionnel du moteur de jeu.
Je tiens également à souligner que vous devriez avoir
requestAnimationFrame()
appelé lorsque vous êtes prêt à repeindre et pas avant (aka à la fin de la boucle de jeu). Un bon exemple de pourquoi vous ne devez pas appelerrequestAnimationFrame()
au début de la boucle est si vous êtes en utilisant une toile de tampon. Si vous appelezrequestAnimationFrame()
au début, puis commencer à dessiner sur le canevas de la mémoire tampon, vous pouvez finir par avoir de tirer la moitié de la nouvelle image, avec l'autre moitié étant l'ancien cadre. Ce qui va se passer sur chaque image en fonction du temps qu'il faut pour terminer le tampon par rapport à la repeindre cycle (60fps). Mais en même temps vous vous retrouvez chevauchement de chaque image afin que le tampon obtenez plus foiré comme il passe en boucle sur son auto. C'est pourquoi vous devez seulement appelerrequestAnimationFrame()
lorsque la mémoire tampon est prêt pour le tirage de la toile. en ayant larequestAnimationFrame()
à la fin, vous pouvez le faire sauter à une mise à jour si le tampon n'est pas prêt à tirer et donc à chaque repeindre est dessiné comme il est prévu. La position derequestAnimationFrame()
dans la boucle de jeu a une grande différence.OriginalL'auteur Patrick W. McMahon
Ici " une mise en œuvre d'un HTML5 système de rendu de l'utilisation d'un pas de temps avec une variable de temps de rendu:
http://jsbin.com/ditad/10/edit?js,sortie
Il est basé sur cet article:
http://gameprogrammingpatterns.com/game-loop.html
Ici est la boucle de jeu:
La
render
fonction appelle unerender
méthode sur chaque sprite, avec une référence à la lagOffsetVoici le sprite de la méthode de rendu qui utilise le décalage offset pour interpoler le sprite de rendu de position sur la toile.
L'important, c'est que ce morceau de code qui utilise la lagOffset et la différence dans le sprite de rendu entre les images pour découvrir son nouveau courant toile position:
Avis que le
oldX
etoldY
les valeurs sont recalculées chaque image à la fin de la méthode, de sorte qu'ils peuvent être utilisés dans le cadre suivant pour aider à comprendre la différence.Je ne suis vraiment pas sûr si cette interpolation est tout à fait correcte ou si c'est la meilleure façon de le faire. Si quelqu'la lecture de ce sait que c'est faux, s'il vous plaît laissez-nous savoir 🙂
OriginalL'auteur d13
La version moderne de requestAnimationFrame envoie maintenant dans un timestamp que vous pouvez utiliser pour calculer le temps écoulé. Lors de votre choix de l'intervalle de temps écoulé, vous pouvez faire votre mise à jour, de créer et de rendre les tâches.
Voici un exemple de code:
requestAnimationFrame
programme une nouvelle boucle, il n'est pas immédiatement déclencher cette boucle. Le reste du code est effectivement atteint. 😉en supposant qu'il continue à exécuter le code. Si vous appelez de l'image suivante avant la fin de la dernière image, puis vous allez gâcher votre 60fps.
requestAnimationFrame()
doit être appelée une fois votre faire avec votre image de sorte que vous pouvez garder une trajectoire en 60fps.Vous remarquerez que tous les exemples de requestAnimationFrame est la dernière chose exécutée dans la boucle, et pour une bonne raison. Au moment de l'appel requestAnimationFrame vous passez à la fonction de l'OS à gérer. Vous souhaitez passer à la fin de l'exécution pour que le système puisse correctement calcule la différence de temps d'exécution. Lors de l'exécution à la fin, le système saura le temps de tours et que vous donnez sur les 60fps où si vous l'exécutez au début, vous ne sera pas obtenir 60fps.
Ma méthode a bien fonctionné pour moi dans mes projets. Je suppose que nous devrons accepter d'être en désaccord! 😉
requestAnimationFrame
donne juste le navigateur de l'autorisation d'exécuter du code à l'intérieur de la boucle, à la plus optimale de temps pour le navigateur. La boucle de code est exécuté à la convenance du navigateur, pas le point quirequestAnimationFrame
utilisés dans le code. Où il est placé à l'intérieur de la boucle est arbitraire, et markE de placement de celui-ci comme la première ligne de la boucle est tout simplement parfait. En fait, il pourrait être le meilleur endroit pour cela, car il indique clairement que le but de la fonction de boucle est et comment il fonctionne.OriginalL'auteur markE
Ce n'est pas vraiment la réponse à votre question, et sans en savoir plus sur le jeu, je ne peux pas dire avec certitude si elle va vous aider, mais avez-vous vraiment besoin de savoir
dt
(ou FPS)?Dans mon limitée incursions dans le JS développement d'un jeu, j'ai remarqué que, souvent, vous n'avez pas vraiment besoin de calculer toute sorte de
dt
comme vous pouvez le viennent généralement avec un sensible la valeur par défaut basé sur votre fréquence d'image, et de faire tout le temps (comme arme de rechargement), il suffit de travailler sur la base du nombre de tiques (c'est à dire un arc peut prendre 60 tiques pour recharger (~1 seconde @ ~60FPS)).J'ai l'habitude d'utiliser
window.setTimeout()
plutôt quewindow.requestAnimationFrame()
, que j'ai trouvé le prévoit, de façon générale une plus grande stabilité de fréquence d'image qui vous permettra de définir une valeur par défaut raisonnable d'utiliser à la place dedt
. Sur le bas-côté, le jeu sera plus une ressource pour les porcs et les moins performants sur des machines plus lentes (ou si l'utilisateur a beaucoup d'autres choses en cours d'exécution), mais en fonction de votre cas d'utilisation peuvent ne pas être vraies préoccupations.Maintenant, c'est purement anecdotique des conseils afin que vous devriez le prendre avec une pincée de sel, mais il a servi de moi assez loin dans le passé. Tout dépend si vous l'esprit, le jeu tourne plus lentement sur les plus/les moins puissantes machines, et l'efficacité de votre boucle de jeu. Si c'est quelque chose de simple qui n'a pas besoin d'afficher en temps réel, vous pourriez être en mesure de faire disparaître
dt
complètement.setTimeout
etsetInterval
et pourquoirequestAnimationFrame
est généralement mieux: goo.gl/PdxHLO. Cependant, je pense que pour la plupart des jeux un débit fixe de logique/de rendu de la boucle (qui vous suggérez) sans calculer le retard et faire le rendu de position d'interpolation est tout simplement parfait.OriginalL'auteur SimonR
À un certain point, vous aurez envie de penser à propos de découplage de votre physique, de votre rendu. Sinon vos joueurs pourraient avoir incompatible physique. Par exemple, quelqu'un avec un costaud de la machine se 300fps aura très accéléré de la physique par rapport à quelqu'un cahin-caha sur à 30fps. Cela peut se manifester dans le premier joueur de croisière autour de dans un mario-like défilement jeu à la super vitesse et l'autre joueur à ramper à la moitié de la vitesse (si vous avez effectué votre test à 60 im /s). Un moyen de corriger cela est de présenter delta du pas de temps. L'idée est que vous trouvez le temps entre chaque image et de l'utiliser comme une partie de votre physique des calculs. Il conserve le gameplay cohérent, indépendamment de la fréquence d'images. Voici un bon article pour vous aider à démarrer: http://gafferongames.com/game-physics/fix-your-timestep/
requestAnimationFrame ne sera pas corriger cette incohérence, mais il est toujours une bonne chose d'utiliser parfois il a des avantages d'économie de la batterie. Voici une source pour plus d'info http://www.chandlerprall.com/2012/06/requestanimationframe-is-not-your-logics-friend/
OriginalL'auteur Spencer
Je n'ai pas vérifier la logique des maths dans votre code .. mais ici ce qui fonctionne pour moi:
Cette mise en œuvre est idenpendant de la CPU vitesse(tiques). J'espère que vous pourrez en faire usage!
window.requestAnimationFrame(this.gameLoop());
- Qui ne fonctionne pas. Vous souhaitez passer une référence àthis.gameLoop
, ne pas l'appeler immédiatement et passer sa valeur de retour derequestAnimationFrame()
, de plus, vous devez être sûrthis
est correct une fois que la fonction est appelée, de sorte qu'il devrait être:window.requestAnimationFrame(this.gameLoop.bind(this));
Grâce fixe, probablement, j'ai fait une faute de frappe dans le post, alors que je cuit + copié de mon jeu où il travaille.
OriginalL'auteur Alex