1

yop

Je suis en train de réfléchir depuis quelques jours à la manière de faire la boucle principale d'un petit jeu.
Alors tout de suite, pour fixer les idées :
- petit jeu 2D de plateforme
- typiquement ce qu'on trouvait sur nos chères TI
- rien de gourmand graphiquement, peut-être des tiles animés à tout péter
- 30 ou 60 fps, je sais pas "ce qu'il faut", mais je pense que ça rentre pas en considération à ce stade. J'aime juste que ce genre de jeu soit assez speed et réactif.

Donc.
Un boucle classique fait 3 choses :
1. process events (on lit le clavier / la souris)
2. update (le moteur, la gravité, les tiles animés, le déplacement du perso, le mouvement éventuel du tilemap toussa
3. le render

Sur TI, on rajoutait un 4. : WaitForSynchro();

Question : est-ce suffisant sur un PC, pour un petit jeu de ce genre ?

Quand je lis des tutos (genre ça : http://gameprogrammingpatterns.com/game-loop.html , http://gamedev.stackexchange.com/questions/651/tips-for-writing-the-main-game-loop , et tous les liens qui vont avec), c'est absolument passionnant, entre autre en ce qui concerne la gestion du temps.
Mais n'est-ce pas complètement overkill dans mon cas ?

En fait, je voudrais répondre à cette question : puis-je rester très simplist, comme j'aurais fait sur TI, ou dois-je dès maintenant prévoir un update (donc le plus gros du moteur) dépendant du temps écoulé, ce qui complexifierait tout de même les choses ?

2

Folco (./1) :
Question : est-ce suffisant sur un PC, pour un petit jeu de ce genre ?


Oui pour faire un jeu simple, ou meme compliqué c'est la "base" d'un gameloop.

Sur certaines libs tu as un "vsync" c'est ce que tu cherches.

Apres tu peux aussi tourner en "roue libre" sans attente, mais tu risque de t'exposer a du tearing, ou alors un moyen simple et con, mais un simple msleep(1/60*1000) (attente pour faire une boucle a 60Hz) peux etre suffisante, nottement quand tu n'as pas moyen de te synchroniser avec l'ecran.

Ce que je conseille c'est de faire un truc comme:

Loop Forever

   vsync();
   Render();
   Event();
   Update();

EndLoop


Et si tu n'a pas de vsync ou equivalent tu peux faire ca:

Delay60Hz = (1/60*1000); // ms
takenTime = 0;
Loop Forever

   msleep(Delay60Hz - takenTime);
   startTime = mili_time();
   Render();
   Event();
   Update();
   takenTime = mili_time() - startTime;
EndLoop


Comme ca le temps prix pour les calcul est prix en compte et si ce temps est variable, ton temps globale de la boucle ne change pas (ou tres peu)

Edit: et il vaux mieux attendre le vsync avant de faire le rendu (ou le blit final si tu travaille dans un buffer) car vsync *normalement* te garantie que l’écran n'est pas dans la phase d'affichage. C'est hérite des CRTs, mais si tu affiche pendant que l'ecran balaye, tu t'expose a du tearing et autres effets du genre.

Ça peux être voulu (genre les rasters) mais ca peux aussi conduire a des effets désagréable (comme le tearing donc)

Edit2: je n;avais pas lu les pages que tu cite, mais c'est exactement ce qu'ils proposent
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

3

Faut utiliser l'Adaptive-Sync embarrassed
avatar

4

Godzil -> ok, je pensais faire un truc aussi simple. La SDL fournit des timers qui se relancent à la demande, suffit d'avoir un booléen volatile que le timer toggle tous les 1/60ème de seconde pour savoir quand lancer le render. Et dans le pire des cas, je crois que la SDL s'occupe toute seule du vsync. Ce qui fait que le timer est là uniquement pour réguler la vitesse du jeu, la lib fait le reste.

5

Folco (./4) :
La SDL fournit des timers qui se relancent à la demande, suffit d'avoir un booléen volatile que le timer toggle tous les 1/60ème de seconde pour savoir quand lancer le render.
Beurk : c'est du polling et c'est mal (ça gaspille du temps CPU et de l'énergie pour rien). Utilise les mécanismes faits pour ça (événements, sémaphores, etc.)

D'autre part, mélanger synchro VBL et synchro par timer t'expose à des soucis, surtout étant donné que les fréquences sont quasi-identiques (en pratique, deux sources indépendantes censées être exactement à la même fréquence ne le sont jamais), et qu'en plus les timers risquent d'être approximatifs.

Il vaut mieux ne se baser que sur la VBL, avec un test au lancement du soft pour déterminer la fréquence de rafraîchissement (tu recalcules tes constantes de timing en fonction).

Note : ça ne serait pas suffisant pour un jeu où le render peut potentiellement prendre plus d'une VBL, mais pour un jeu 2D sur une machine normale y'a peu de risques et ça simplifie les choses. Idem, avoir des constantes de timing différentes peut poser problème dans les moteurs physiques compliqués et/ou pour du jeu en réseau où les résultats doivent être identiques sur toutes les machines.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

6

Le seul soucis c'est que vblank/vsync sur certaines lib et dans certains contexte ne fait.... rien :/

(et ma proposition de "timer" n'est que pour se passer de vsync/vblank, pas mixer les deux)

Vsync normalement te place au moment ou le CRT est dans la partie VBLANK donc qu'il n'affiche rien, et c'est la periode privilegié pour afficher. Une fois le rendu fait, on peux ensuite prendre tout le temps qu'on veux pour gerer la mecanique du jeu et une fois fait attendre le prochain vblank.
Zerosquare (./5) :
et qu'en plus les timers risquent d'être approximatifs.


Sur une archi multithread/multiprocess ils seront approximatif c'est certain, sur TI-Nes j'etait aller jusqu'a mesurer le délais réel du usleep que j'utilisait pour compenser au prochain cycle le délais manquant ou en trop.

Ca marchait plutot bien grin

Par contre il fuat absolument borner le delta dans ce cas sinon des choses comme supprimer temporairement le délais, ou mettre en pause peux avoir des effets plutôt sympa grin
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

7

Zerosquare (./5) :
Beurk : c'est du polling et c'est mal (ça gaspille du temps CPU et de l'énergie pour rien). Utilise les mécanismes faits pour ça (événements, sémaphores, etc.)

Oué c'est vrai, SDL_FilterEvent("mon event custom") / SDL_waitEvent(&ev); oui

8

Tiens, sur la mailing list de SDL, ce matin :
SDL_RenderPresent(renderer) might be waiting for Vsync, that's probably why the time is so high. Do all your game loop work before calling SDL_RenderPresent(), because it will block while waiting for Vsync. To test whether that is indeed happening, measure the time taken by SDL_RenderPresent().

SDL_Present(renderer* rdr) semble faire de la synchro sur le vsync, donc même pas besoin de timer ou autre : c'est intégré. smile