Bonjour !
J'aimerais écrire un moteur de jeu tile-based pour la SFML, en utilisant cette architecture, centrée sur un "Message Bus".
http://www.gamasutra.com/blogs/MichaelKissner/20151027/257369/Writing_a_Game_Engine_from_Scratch__Part_1_Messaging.phpPour implémenter cela, j'aurais besoin d'une fonction "tick" appelée plusieurs fois par secondes, qui sera utilisé pour "rafraichir" les systèmes. (Leur permettre de lire les messages et d'en poster des nouveaux)
Cette fonction doit tourner le plus de fois possible chaque seconde, indépendamment des FPS. (Pour éviter que le jeu ne soit plus "réactif" sur un écran 144Hz que sur un écran 60hz (VSync activée))
J'ai donc essayé quelque chose : faire 2 Threads, un pour le rendering et un pour le "tick", avec ce code ci : (Je fais des tests pour le moment (pour planifier tout correctement avant de me lancer dans le développement), bien sur ce n'est pas le code du moteur ^^)
#include <SFML/Graphics.hpp>
#include <iostream>
#include "Screen.h"
#include <thread>
using namespace std;
void drawingThread(sf::RenderWindow *window)
{
while (window->isOpen())
{
sf::Event event;
while (window->pollEvent(event))
{
if (event.type == sf::Event::Closed)
window->close();
}
window->clear(sf::Color::Black);
window->display();
}
}
void tick(const sf::RenderWindow *rw)
{
while(rw->isOpen())
cout << "Tick!" << endl;
}
int main()
{
// create the window
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
thread t1(drawingThread, &window);
thread t2(tick,&window);
t1.join();
t2.join();
cin.get();
return 0;
}
Cela ne marche pas, j'ai une erreur "Failed to Activate the window context" ou quelque chose comme cela.
J'essaie donc autre chose :Garder le rendering dans le thread principal, et décaler le "tick" dans un autre thread.
#include <SFML/Graphics.hpp>
#include <iostream>
#include "Screen.h"
#include <thread>
using namespace std;
void tick(const sf::RenderWindow *rw)
{
while(rw->isOpen())
cout << "Tick!" << endl;
}
int main()
{
// create the window
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
thread t2(tick,&window);
t2.join();
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color::Black);
window.display();
}
cin.get();
return 0;
}
Cela ne marches toujours pas ! Ma fenêtre reste blanche, inanimée..
Je pense que cela est surtout dû à une incompréhension des threads de ma part (et peut être aussi une mauvaise utilisation des pointeurs?) -> le pointeur n'agis pas comme je le pense, et "bloque" l'instance de RenderWindow, mais comment puis-je résoudre cela ? Quelle est la bonne "méthode" à adopter ?
Egalement, j'en profite pour poser une question sur les threads en général. Dans ma classe "MessageBus" j'aurais, probablement, des pointeurs vers les différent systèmes. Et, depuis le thread "tick" je devrais appeler une fonction "refresh" (le tick) dans la classe MessageBus, j'aurais donc un code ressemblant à cela :
#include <vector>
using namespace std;
class GESystem
{
// La Classe "modèle" pour chaque système, qui permet de faire du polymorphisme.
//...
public:
virtual void tick() = 0;
};
class MessageBus
{
public:
vector<GESystem *> sys;
void refresh()
{
for(int k(0);k<sys.size();k++)
sys[k]->tick();
}
// Note : Dans cette fonction tous les modules sont rafraichis, sauf le module "Render", qui lui est rafraîchi seulement quand on a besoin d'afficher quelque chose à l'écran (faire le rendu !)
};
// Plus loin dans le code
void tickthread(MessageBus *mb)
{
while(window.isOpen())
mb->refresh();
}
(Avec tickthread qui sera la fonction utilisée par le Thread pour appeler la fonction "refresh" sur l'instance de MessageBus (utilisée par le moteur de jeu) le plus de fois possible chaque seconde.)
Cette structure est-elle correcte, et marchera t-elle avec les threads ? Je ne connais pas bien les threads, donc je ne sais pas si cela va poser problème ou pas. Si cela pose problème, comment avoir une architecture saine avec ce genre de prérequis ? (Nécessite d'appeler une fonction sur une instance de MessageBus du thread principal)
D'une manière plus générale : Cela pose problème d'appeler une méthode d'une instance d'une classe située dans un autre thread (On accède à l'instance via un pointeur) ?
(Mon dieu, j'espère que j'explique bien la situation ici xD)
Pour finir, j'ai l'impression que cette architecture est peu adaptée à cette situation, dois-je plutôt essayer de revoir toute mon architecture plutôt que de résoudre ces problèmes ? (Si vous avez d'autres exemples d'architecture, je suis preneur ^^)
Ou alors, méthode simple : Je limite le jeu à, par exemple, 60FPS peu importe la situation, comme cela je fait tout dans la boucle principale. (Note : je n'aime pas cette solution, car cela implique de lier toute la logique du moteur de jeu aux FPS et je ne trouve pas cela "sain", surtout en ce qui concernera les animations..)
EDIT : Dernière question. Est-ce grave si le jeu est plus "réactif" car il tourne à 144hz et pas 60hz ? Je veux dire, est-ce normal ? Les autres moteurs de jeu agissent aussi comme cela ? (La GameLoop est liée aux FPS ?)
Mes explications sont probablement peu claires, je reste donc à votre disposition pour toute question.
Merci