Bonjour à tous !
Je suis en train de travailler sur un petit projet ici : http://fr.sfml-dev.org/forums/index.php?topic=12617.0
Et je viens de réfléchir à comment simplifier au maximum ma fonction main (j'ai sûrement pas encore uploadé ma dernière version)
Donc premièrement je suis venu repenser l'ordre d'affichage "traditionel" qui est :
window.clear()
window.draw(...)
window.display()
Je me suis dit que finalement, vu que la boucle se lit "vite", que le code du haut et celui-ci revenait au même :
window.draw(...)
window.display()
window.clear()
J'ai fait des tests, aucuns problèmes :)
Maintenant c'est la que j'ai poussé à la simplification "extrême", j'ai ajouté les fonctions display() et clear() dans la fonction isOpen() comme ceci :
bool Application::isOpen()
{
bool running = mWindow.isOpen();
display();
clear();
return running;
}
//Dans mon projet, je travaille sur une classe Application qui comporte une RenderWindow et ses méthodes (que je peux donc modifer)
et donc après ça je peux dessiner tranquillement dans ma boucle isOpen() sans me soucier des display et clear :)
Bon voilà c'est pas grand chose mais finalement quand on compte le nombre de fois qu'on écrit ces fonctions :)
Ensuite j'ai essayé de voir les avantages de "l'ancienne" méthode, finalement le seul inconvénient c'est le fait que clear peut prendre un paramètre... Mais en fait, il suffit d'ajouter la couleur du clear en paramètre à isOpen() ;)
Voilà, je pense que cette petite chose peut être simplifiée :)
Juste pour la petite histoire, l'ordre optimal serait même celui-ci :
display()
...
update()
...
clear()
draw()
Ce qui revient au bon vieux
update()
...
clear()
draw()
display()
... car lors de l'appel à display(), le GPU se "met en route" et exécute tout le boulot qui a potentiellement été mis en attente par les appels précédents à draw(). Donc après display(), si le CPU attaque directement par une instruction graphique, il va devoir attendre que le GPU ait terminé et donc bloquer à ne rien faire. Alors que si le CPU s'occupe de choses qui n'impliquent pas le GPU pendant ce temps, les deux peuvent tourner en parallèle, d'où un gain de temps.
Bien sûr c'est pertinent pour de grosses applications graphiques avec plein de calculs GPU et CPU ; là on s'en fiche un peu ;)
Sinon, pour en revenir à ta proposition, c'est une très mauvaise idée.
Premièrement, isOpen() indique l'état de la fenêtre, elle n'est pas censée faire quelque chose de manière cachée. Ce serait mentir à l'utilisateur, et quand on ment à l'utilisateur on peut être sûr qu'il va coder des bêtises à son insu.
Ensuite isOpen() peut être appelée n'importe quand, le fait que ce soit la condition de la boucle principale et que ce ne soit appelé nulle part ailleurs, c'est juste le cas simple. Qu'est-ce qui empêche d'appeler isOpen() pour vérifier si al fenêtre est ouverte ailleurs ? Qu'est-ce qui empêche d'utiliser un booléen et de ne jamais l'appeler ? Pense aux programmes complexes, avec plusieurs threads, etc.
Et puis enfin il faut laisser la flexibilité à l'utilisateur. Ce schéma n'est pas figé, il y a des gens qui utiliseront ces trois fonctions très différemment.
Quand à un isOpen(sf::Color::Black)... sans déconner ? :P
Très bon arguments, je m'incline :)
Le premier est le moins percutant mais je comprends que pour une lib de cette qualité ce n'est pas possible.
J'avoue ne pas du tout avoir pensé au second...
Et le troisième je savais que ça n'avait pas de sens mais c'était plus pour le côté "pratique" xD
Pourquoi ne pas ajouter alors une fonction isRunning() qui ferait ça, du coup 1-On ne ment pas car elle est prévue pour, 2-Bah du coup elle est prévue pour donc les gens peuvent utiliser isOpen() quand même pour leurs tests, 3-Tant pis pour la couleur en argument ou une couleur par défaut
Je te laisse jeter un coup d'oeil à mon code où justement j'utilise cette technique, même si comme tu me le dis avec l'exemple du CPU/GPU, ici c'est une petite application et que pour les grosses cela ne fonctionne pas terriblement bien...
#include <SFML/Graphics.hpp>
#include "include/Application.h"
int main()
{
Application app;
BoutonText bouton = app.newBoutonText(app.getTexture("gui.png"),sf::IntRect(0,0,396,35),sf::IntRect(0,35,396,35),sf::IntRect(0,70,396,35),sf::Vector2f(0,0),4,10,"Clique pour centrer",23,app.getFont("cloister.ttf"),sf::Color(255,255,255));
BoutonImage boutonI = app.newBoutonImage(app.getTexture("gui.png"),sf::IntRect(0,0,396,35),sf::IntRect(0,35,396,35),sf::IntRect(0,70,396,35),sf::Vector2f(50,50),4,10,app.getTexture("gui.png"),sf::IntRect(0,70,180,10));
while (app.isOpen()) //Application::isOpen inclut les fonctions display, clear et drawGui
{
while (app.pollEvent()) //Application::pollEvent inclut la gestion de la fermeture de la fenêtre mais ça c'est encore autre chose et je comprends que tu ne veuilles pas
{
if (CmEvent::clicLeft(app.getEvent()) && bouton.isHover(app.getWindowPtr()))
{
bouton.centerText("Bim! Centré!");
}
if (CmEvent::clicLeft(app.getEvent()) && boutonI.isHover(app.getWindowPtr()))
{
boutonI.centerImage();
}
}
}
return 0;
}
Juste pour la petite histoire, l'ordre optimal serait même celui-ci :
display()
...
update()
...
clear()
draw()
Ce qui revient au bon vieux
update()
...
clear()
draw()
display()
... car lors de l'appel à display(), le GPU se "met en route" et exécute tout le boulot qui a potentiellement été mis en attente par les appels précédents à draw(). Donc après display(), si le CPU attaque directement par une instruction graphique, il va devoir attendre que le GPU ait terminé et donc bloquer à ne rien faire. Alors que si le CPU s'occupe de choses qui n'impliquent pas le GPU pendant ce temps, les deux peuvent tourner en parallèle, d'où un gain de temps.
Sauf que l'appel à "display" entraîne un glFinish implicite, bloquant par nature.
Et de toute façon, c'est déjà parallélisé, le GPU tourne en même temps que le CPU lui donne des instructions, l'idéal serait alors de faire les gros calculs CPU après le draw() :)
Bon en revanche ça introduit une frame de latence pour le joueur, ce qui peut avoir son importance selon le type de jeu.