JeuWeb - Crée ton jeu par navigateur
Game loop en javascript - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Game loop en javascript (/showthread.php?tid=7081)

Pages : 1 2


Game loop en javascript - Aleskweb - 05-08-2013

Bonjour,

J'aimerais avoir votre avis sur le sujet suivant.

Pour un jeu en html5 avec un moteur physique (box2Dweb ) ainsi qu'une partie graphique (Canvas ou webgl) , quelle serait la loop la plus optimisee?
(Sachant que box2Dweb fonctionne mieux si son world.step est executé avec un framerate constant.)

J'ai globalement lu de tout, le setInterval() est pas mal utilisé. Je l'ai testé et ce n'est pas concluant. ( Placé en parallele avec une autre loop graphique basee sur requestanimationframe ) Resultat : graphiquement c'est fluide, les animations tournent a la vitesse max. En revanche le moteur 2D subit de gros ralentissements : en fesant des tests de temps entre deux frames, sur un setInterval a 60fps, on obtient des deltas allant de 8ms a parfois plus de 35. Du grand n'importe quoi :p (Mon code est peut etre pas le plus optimisé aussi)

Bref j'ai aussi vu l'utilisation de requestanimationframe pour la loop du moteur physique. C'est certainement une bonne idee puisque les perfs de cette fonction sont meilleures je crois. En revanche, il faut gerer sois meme la boucle pour un framerate constant.

Je pensais donc a regrouper les deux boucles en une seule fonctionnant avec requestanimationframe avec un ordre suivant

-Step du moteur physique
-Traitement
-Rendu graphique

Mais ca me fait un peu peur car le framerate supposé constant du moteur physique pourrait etre ralenti par le rendu graphique. Et a partir du moment ou le moteur physique ralentis, on a plus les meme positions entre la simulation coté serveur et celle coté client.

Voila, qu'en pensez vous?


RE: Game loop en javascript - Xenos - 05-08-2013

Un moteur physique à framerate variable va être ingérable... Il me semble que, par exemple, requestAnimationFrame peut attendre quasi-indéfiniement si jamais l'utilisateur est passé sur un autre onglet. Tu peux alors te retrouver avec un intervalle de plusieurs secondes (voire minutes). Ajouter un système dans la fonction lancée par rAF, pour découper l'intervalle de temps en sous-intervalle de X ms max chacune, pourrait alors être une solution... mais ce sera lourd! Suppose que l'onglet soit inactif 10 minutes, et que l'intervalle max soit de 50ms (20fps), on aurait alors 12.000 frames physiques à calculer d'un coup :o
Tout fusionner en un bloc ne me semble pas approprié quand même, car alors, l'un ralenti l'autre et on perd l'intérêt d'avoir la partie graphique pour le GPU et la partie physique pour le CPU (le GPU doit attendre que le CPU ait finit avant de savoir quoi afficher).

Si setInterval ne te plait pas, essaie plutôt un setTimeOut à la fin du loop physique. Mesure le temps entre deux loops physiques et fait les calculs en conséquence.

Pour la désynchronisation client / serveur, je ne me reposerai pas trop dessus quand même, surtout si tu n'as pas de marqueur absolue (le timestamp en ms par exemple). Si tu n'as que des marqueurs relatifs (le serveur fait un wait de 200ms puis calcule et recommence; le client fait un wait de 200ms, puis calcule puis recommence) alors tu as un soucis de synchronisation qui arrivera au niveau du calcul: le serveur ira plus ou moins vite que le client pour faire son calcul. Et si tu dis "toutes les 200ms, le serveur lance le calcul, toutes les 200ms, le client lance le calcul", alors il faudra t'assurer que le calcul ne dépassera pas 200ms, sinon, il y aura un loop infini. Les durées de calcul ne sont pas des données sur lesquels on devrait se reposer (je ne sais plus d'où sort cette "règle").


RE: Game loop en javascript - Aleskweb - 05-08-2013

Merci pour cette reponse,

En fait sur le serveur je fais tourner la meme simulation que coté client ( je pourrais d'ailleurs le faire tourner a 20fps ca changerais rien). Mais le serveur arrive a avoir un framerate constant avec le setInterval (pour 60fps ca tourne de 16 a 17ms)
Par contre coté client impossible d'avoir quelque chose de constant. J'ai l'impression que le rendu 3D provoque des ralentissements. C'est pourtant sencé etre asynchrone non?

A chaque action d'un joueur je repositionne le joueur (client) avec la position de ce meme joueur cote serveur. Mais vu que le client a des ralentissements, le perso se teleporte plus loin a chaque fois.

Ya toujours la methode barbare d'envoyer la position de chaque joueur avec un socket.emit toutes les 16ms. Ca marche mais a 10 joueurs le serveur va claquer a mon avis https://github.com/logsol/box2dweb-network-synchronisation

Buildnewgame.com propose une loop avec rAF :

Physics.prototype.step = function (dt) {
this.dtRemaining += dt;
while (this.dtRemaining > this.stepAmount) {
this.dtRemaining -= this.stepAmount;
this.world.Step(this.stepAmount,
8, // velocity iterations
3); // position iterations
}
if (this.debugDraw) {
this.world.DrawDebugData();
}
}

Mais comme tu l'as dit le while va etre lourd. Le top serait d'arriver a quelque chose de constant avec setInterval


RE: Game loop en javascript - Maks - 05-08-2013

Citation :Par contre coté client impossible d'avoir quelque chose de constant.

Il faut utiliser le deltatime

Citation :J'ai l'impression que le rendu 3D provoque des ralentissements. C'est pourtant sencé etre asynchrone non?

Tu as essayé les web workers ?

Citation :Ya toujours la methode barbare d'envoyer la position de chaque joueur avec un socket.emit toutes les 16ms. Ca marche mais a 10 joueurs le serveur va claquer a mon avis https://github.com/logsol/box2dweb-netwo...ronisation

C'est ce qu'il faut faire, faut juste optimiser la socket

En ce qui concerne la boucle, moi j'en ferais qu'une seule. Pour mon jeu j'en ai une coté client, une coté serveur et c'est tout.
Envoie un fiddle voir ce que ça donne Smile

Du code et de la lecture pour toi

http://www.youtube.com/watch?v=Prkyd5n0P7k
http://code.google.com/p/gritsgame/ (exactement ce que tu veux faire je pense)

http://buildnewgames.com/optimizing-websockets-bandwidth/
http://blogs.msdn.com/b/davrous/archive/2011/07/08/introduction-aux-web-workers-d-html5-le-multithreading-version-javascript.aspx (dernier chapitre)


RE: Game loop en javascript - Aleskweb - 05-08-2013

Merci beaucoup

Eh bien normalement setInterval devrait donner quelque chose de constant non? Sinon faudrait que j'utilise le delta time et que je rappelle la fonction avec un setTimeOut. ( En espérant que ce dernier marche mieux que setInterval )

Pour les webworkers, tres bonne idée, donc en gros je ferais tourner le moteur physique dans un thread a part?

Sinon, j'utilise three.js pour ma part, les rendus sont un peu plus long a mon avis. (Meme pour du low poly).
Je n'ai pas pu tester grits car apparement aucun serveur n'est dispo. Mais d'après la video, c'est a peu près le gameplay.

pour le fiddle ca risque d'etre compliqué, mais pour le moment, ca peut être accessible sur http://213.251.147.29/ si on a lancé le serveur
(id: visiteur1 mdp: visiteur) Si ca se connecte pas, essayez avec visiteur2

Questions en vracs:
Replacer un joueur toutes les 200ms ne serait pas déja suffisant? (a 32 ms il y aurait pratiquement meme pas besoin de moteur physique coté client).
Tu préconise une seule boucle (calcul physique et affichage dans la meme?) ? Donc du séquentiel plutôt que du parallèle, pourquoi?


Merci


RE: Game loop en javascript - Maks - 05-08-2013

Citation :Eh bien normalement setInterval devrait donner quelque chose de constant non? Sinon faudrait que j'utilise le delta time et que je rappelle la fonction avec un setTimeOut. ( En espérant que ce dernier marche mieux que setInterval )

Il me semble que RAF est plus constant que setInterval justement (explications à retrouver, une histoire de frame de mémoire). Oui il faut utiliser le deltatime à chaque fois que tu dois incrémenter une variable (les coordonnées d'un joueur par exemple). Pour Box2D je sais pas comment ça se passe, peut être qu'ils ont prévu quelque chose ? Ou alors c'est à toi de t'en occuper avec leur API.

Citation :Pour les webworkers, tres bonne idée, donc en gros je ferais tourner le moteur physique dans un thread a part?

Tout à fait, c'est parfait pour les gros calculs. Sur l'exemple que je t'ai donné il utilise ça pour son raycasting. il D'autant plus que si tu utilises WebGL pas de soucis pour utiliser les Web Workers niveau compatibilité donc faut pas t'en priver ^^

Citation :Sinon, j'utilise three.js pour ma part, les rendus sont un peu plus long a mon avis. (Meme pour du low poly).
Je n'ai pas pu tester grits car apparement aucun serveur n'est dispo. Mais d'après la video, c'est a peu près le gameplay.

Surtout regarde la vidéo comprendre comment ça marche et le code source, c'est le même cas d'utilisation que toi : WebGL, websockets et moteur 2D Smile
Dans la vidéo ils donnent vraiment des astuces indispensables pour ce genre de jeu assez ambitieux (la 3D + collisions = beaucoup plus de problèmes à gérer ^^)

Citation :pour le fiddle ca risque d'etre compliqué, mais pour le moment, ca peut être accessible sur http://213.251.147.29/ si on a lancé le serveur
(id: visiteur1 mdp: visiteur) Si ca se connecte pas, essayez avec visiteur2

J'ai essayé c'est un bon début j'aime bien les robots Big Grin Mais sinon ça saccade un peu en effet

Citation :Questions en vracs:
Replacer un joueur toutes les 200ms ne serait pas déja suffisant? (a 32 ms il y aurait pratiquement meme pas besoin de moteur physique coté client).

Sur BuildNewGames, va sur l'article Realtime Multiplayer HTML5 il donne les fréquences exactes (45 ms je crois, je suis plus sur). Mais 200ms ça sera beaucoup trop long à cause des collisions des attaques par exemple.

Tes packets ressembleront à ça avec un moteur 2D :


var state = [{
id: 0,
transform: {
position: {
x: 15.290663048624992,
y: 2.0000000004989023,
z: -24.90756910131313
},
rotation: {
x: 0.32514392007855847,
y: -0.8798439564294107,
z: 0.32514392007855847,
w: 0.12015604357058937
}
}
}, {
id: 1,
transform: {
position: {
x: 7.490254936274141,
y: 2.0000000004989023,
z: -14.188117316225544
},
rotation: {
x: 0,
y: 0.018308020720336753,
z: 0.1830802072033675,
w: 0.9829274917854702
}
}
}];

(à optimiser bien sur)

Citation :Tu préconise une seule boucle (calcul physique et affichage dans la meme?) ? Donc du séquentiel plutôt que du parallèle, pourquoi?

Si tu fais deux timers, ils ne s’exécuteront pas en parallèle, ils s'exécuteront à la suite après avoir été pushé dans la queue des events, c'est dans la nature de JS. C'est la même chose pour tes calculs, ils ne s’exécuteront pas en parallèle, il faut que tu utilises les web workers Smile


RE: Game loop en javascript - Aleskweb - 05-08-2013

Merci beaucoup Smile

Citation :Il me semble que RAF est plus constant que setInterval justement (explications à retrouver, une histoire de frame de mémoire). Oui il faut utiliser le deltatime à chaque fois que tu dois incrémenter une variable (les coordonnées d'un joueur par exemple).

Pour le deltatime du rendu graphique, je m'en sers pour update les animations mais je ne comprends pas pourquoi il faudrait s'en servir pour les coordonnées des joueurs (puisques celles ci sont données par le moteur physique et ce dernier a un deltatime constant normalement)

En revanche pour la position, arrondir a 2 chiffre après la virgule ferait pas mal d'économies a mon avis.

Oui sinon la video est super intéressante, jvais jeter un oeil au code source mais ce sera peut être plus dur de choper le fonctionnement global.

Pour ce qui est des web workers, en utilisant box2dweb dans un thread. Je ne pourrais plus accéder directement au world directement pour creer des objets. Je devrai donc faire un système d'event listener qui recevra la demande de création. Et au lieu de demander la position de chaque body au moteur physique avec GetPosition(), c'est lui qui enverra au script principal les positions des body a chaque step. Ais-je bien compris?



Citation :J'ai essayé c'est un bon début j'aime bien les robots 1 Mais sinon ça saccade un peu en effet

Bon en fait c'est un modele md2 sorti de je ne sais quel jeu converti en format json. A la base c'est un chevalier mais sans épée il ressemble plus a un robot :p


RE: Game loop en javascript - Maks - 05-08-2013

Citation :Pour le deltatime du rendu graphique, je m'en sers pour update les animations mais je ne comprends pas pourquoi il faudrait s'en servir pour les coordonnées des joueurs (puisques celles ci sont données par le moteur physique et ce dernier a un deltatime constant normalement)

Hum c'est possible, à vrai dire comme je n'ai l'ai jamais essayé... Il y a cet article : http://gafferongames.com/game-physics/fix-your-timestep/ la fonction ressemble à celle que tu as posté. Essaye de chiner dans le code de Grits comment ils ont fait Smile (ils ont aussi utilisé box2d web de mémoire)

Citation :En revanche pour la position, arrondir a 2 chiffre après la virgule ferait pas mal d'économies a mon avis.

C'est une des pistes de l'article avec les tableaux typés aussi

Citation :Pour ce qui est des web workers, en utilisant box2dweb dans un thread. Je ne pourrais plus accéder directement au world directement pour creer des objets. Je devrai donc faire un système d'event listener qui recevra la demande de création. Et au lieu de demander la position de chaque body au moteur physique avec GetPosition(), c'est lui qui enverra au script principal les positions des body a chaque step. Ais-je bien compris?

Oui c'est déjà prévu comme cela, il y a un émetteur et un listener à mettre en place de chaque côté ^^ L'API est assez simple Smile

Citation :Bon en fait c'est un modele md2 sorti de je ne sais quel jeu converti en format json. A la base c'est un chevalier mais sans épée il ressemble plus a un robot

Ahah oui j'ai pas fait gaffe au bouclier aussi !


RE: Game loop en javascript - Aleskweb - 05-08-2013

Peut etre que le Semi-fixed timestep pourrait etre une bonne idée pour les mauvais ordinateurs. A tester Smile

Petite question : Sans les webs workers, je créais le body dans la classe Perso(), et j'avais donc accès au body avec le this._body.
Maintenant, le monde physique etant dans un worker a part, ce ne sera plus possible d'attacher directement le body au personnage.
Penses tu que cette separation pourrait causer des problemes dans la futur, ou alors que la separation complete est une bonne chose?


RE: Game loop en javascript - Maks - 06-08-2013

Je sais pas trop comment ça marche mais body est un attribut immuable ou non ? Dans le worker tu dois surtout effectuer les calculs couteux en ressource, je pense pas qu'il faille forcément tout déplacer ^^

Pour ce qui est de la séparation, séparer la logique de dessin, physique et de gestion du personnages (points de vie, attaque, défense ect) c'est mieux en effet, après faut voir si à un moment tu as besoin d'un attribut, c'est difficile à dire sans bout de code et sans connaître la librairie (je vais essayer de me pencher un peu dessus pour voir si je trouve le temps, j'avais fait que survoler).

Essaye de tester un peu tout ça déjà voir si tu arrives à une meilleure fluidité Smile