-
Bonjour,
Alors voilà tout d'abord je ne sais pas si ce post est à mettre dans la partie Graphique ou Fenêtre mais j'ai choisi le forum Fenêtrage car le problème est en lien avec ceci.
Donc j'ai un problème lorsque je clique sur la barre de la fenêtre ou lorsque je la déplace. En effet, je gère la collision d'un sprite et lorsque je fais une des 2 actions ci-dessus, le système de collision est comme "endormi" c'est-à-dire qu'il ne marche plus jusqu'à ce que j’arrête de cliquer ou déplacer la fenêtre.
Voici un code qui fait cela :
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::RenderWindow App(sf::VideoMode(800, 600, 32), "SFML Window");
sf::Image Image, image2;
if (!Image.LoadFromFile("test.png"))
return EXIT_FAILURE;
image2.LoadFromFile("test2.png");
sf::Sprite Sprite(Image);
//Sprite.SetBlendMode(sf::Blend::None);
Sprite.SetPosition(200.f, 500.f);
int ok = 0;
int descente = 0;
int v = 0;
sf::Image BackgroundImage;
if (!BackgroundImage.LoadFromFile("background.jpg"))
return EXIT_FAILURE;
while (App.IsOpened())
{
sf::Event Event;
while (App.GetEvent(Event))
{
if (Event.Type == sf::Event::Closed)// Fenêtre fermée
App.Close();
if(Event.Type == sf::Event::KeyPressed && Event.Key.Code == sf::Key::Up)
ok = 1;
if(Event.Type == sf::Event::KeyPressed && Event.Key.Code == sf::Key::Down)
{
sf::Sprite s;
s.SetImage(image2);
Sprite = s;
}
}
float ElapsedTime = App.GetFrameTime();
if (App.GetInput().IsKeyDown(sf::Key::Right)) Sprite.Move( 110 * ElapsedTime, 0);
if (App.GetInput().IsKeyDown(sf::Key::Left)) Sprite.Move( -110 * ElapsedTime, 0);
if(ok == 1)
{
if(descente == 0)
{
if(Sprite.GetPosition().y > 425)
{
Sprite.Move(0, (-100+v) * ElapsedTime);
v -= 1;
}
else
{
descente = 1;
}
}
else
{
if(Sprite.GetPosition().y < 500)
{
Sprite.Move(0, (100+v) * ElapsedTime);
v += 1;
}
else
{
descente = 0;
ok = 0;
v = 0;
}
}
}
App.Clear();
App.Draw(Sprite);
App.Display();
}
return EXIT_SUCCESS;
}
C'est un code que j'avais fait pour tester la SFML pour la première fois.
Savez-vous comment régler le problème car, à moins d'imposer le fullscreen, si l'utilisateur bouge la fenêtre le sprite et les pnj du jeu passeront à travers les murs ?
Merci.
-
Il n'y a pas que les collisions qui sont bloquées, c'est tout ton thread principal qui l'est. La seule façon de continuer à tourner pendant le déplacement / redimensionnement de la fenêtre, c'est d'avoir du code dans un autre thread que celui qui gère les évènements.
-
Oui enfin là c'était pour l'exemple et merci pour la précision.
Mais dans ce cas, pour que l'application continue normalement, dois-je copier le thread principal dans le thread secondaire ? Et faut-il l'activer dès que le thread principal est bloqué (si on peut savoir) ?
EDITE: Ou peut-être faut il juste appeler la fonction collision et l'associer au thread secondaire ?
-
Après tu t'organises comme tu veux, ça dépend de ton application, mais en gros la c'est la fonction GetEvent qui va bloquer ; donc tout ce que tu veux qui ne bloque pas, il faut que ce soit dans un autre thread que celui qui appelle GetEvent.
-
OK merci !
En fait, je dois faire "tourner" le thread secondaire tout le temps ?
J'ai une dernière question :
Est ce que le fait de faire une classe Thread secondaire fait la même chose que créer un sf::Thread et d'appeler une fonction qui tourne en boucle du type :
void Fonction(void* UserData)
{
bool value(true);
while(value)
{
// Si on veut arreter la boucle
value = false;
}
}
-
J'ai pas très bien compris ta question.
Mais est-ce que tu es à l'aise avec les threads ? C'est un peu dangereux de les utiliser sans bien maîtriser le sujet -- même si je sais bien que c'est la seule solution pour régler ce problème-là.
-
À vrai dire, je ne m'en suis jamais servi.
En fait si j'ai bien compris son principe, il permet de faire d'autres instructions en même temps que le thread principal.
Mais dans les exemples du tutoriel, il ne fait qu'afficher 10 fois "Je suis le thread numero 1". Or je veux l'utiliser pour gérer les collisions, donc je dois bien le faire tourner en boucle vu que l'on appelle qu'une seule fois la fonction Launch() ?
-
Oui c'est ça, ton thread secondaire doit boucler indéfiniment, de manière parallèle à la boucle d'évènements.
-
Merci pour la réponse !
Mais comment fait-on pour passer un paramètre à la fonction car j'aimerai qu'elle est accès au sf::RenderWindow ? J'ai du mal à utiliser le code du tutoriel suivant :
void ThreadFunction(void* UserData)
{
// Transtypage du paramètre en son type réel
MyClass* Object = static_cast<MyClass*>(UserData);
}
En gros, je devrais faire :
int Classe::Run(sf::RenderWindow &App)
{
Running = true;
sf::Thread Thread(&ThreadFonction, &App);
Thread.Launch();
while(Running)
{
}
//....
}
void Classe::ThreadFonction(void *UserData)
{
sf::RenderWindow* App = static_cast<sf::RenderWindow*>(UserData);
while(Running)
{
}
}
Mais ça ne marche pas, j'ai une erreur :
error C2276: '&' : opération non conforme sur l'expression d'une fonction membre liée
-
Lis le tutoriel, c'est expliqué.
-
C'est ce que j'ai fait mais là je trouve pas ... Ça bloque ici:
sf::Thread Thread(&ThreadFonction, &App);
Avec l'erreur sur mon post précédent.
Faut-il obligatoirement que la fonction ThreadFonction(void* UserData) soit globale ?
-
Il faut utiliser ll'approche décrite dans le paragraphe "Utilisation de sf::Thread en tant que classe de base". La première approche ne marche que pour les fonctions non-membres.
-
Merci beaucoup !
Et donc vu que je gère le déplacement du personnage & ceux des pnj, je dois utiliser des pointeurs dans le thread pour qu'ils pointent vers le perso, les pnj et le sf::RenderWindow pour les utiliser ?
Ça me parait être le seul moyen ...
-
Pas forcément, tu peux accéder à n'importe quelle variable depuis un thread, pas besoin de lui passer en paramètre.
-
Oui mais dans mon cas, j'utilise cette methode (http://www.sfml-dev.org/wiki/fr/tutoriels/multiecransjeu) pour gérer un menu, l'interface de jeu etc...
Et donc de cette façon, je devrais hériter la classe Screen de sf::Thread ? Car au début, j'étais parti pour créer un membre sf::Thread d'une classe Jeu qui hérite de Screen. Or dans ce cas là, je ne pouvais pas accéder aux autres membres de la classe Jeu (c'est à dire le personnage, les pnj ...)
-
De toute façon oui, avec SFML 1.6 tu es obligé de faire dériver de sf::Thread la classe qui "fait les trucs threadés".
-
Pas avec SFML 2.0 ?
Merci pour tes réponse ! Je vais essayer comme ça.
-
Avec SFML 2 déjà tu ne dérives plus de sf::Thread.
-
OK mais j'ai encore un problème. Y a t'il une solution pour envoyer un sf:RenderWindow en paramètre pour la classe qui hérite de sf::Thread ? Car les "Screen" ont pour fonction virtual int Run(sf::RenderWindow &App) = 0;
Or si je tappe:
sf::RenderWindow App(...);
Screen t;
t.Run(App);
Ça ne va pas lancer le thread car ça ne marche pas, le programme arrête totalement de fonctionner lorsque qu'on bouge la fenêtre...
-
Tu veux faire ça ?
// ça c'est le point d'entrée public
void Screen::Run(sf::RenderWindow& window)
{
m_window = &window;
Launch();
}
// et ça c'est la fonction threadée
void Screen::Run()
{
// use m_window ...
}
-
Oui, c'est simple en fait mais j'ai une erreur lorsque l'attribut sf::RenderWindow de la classe Screen fait réference à sf::RenderWindow& window :
error C2758: 'Screen::App' : doit être initialisé(e) dans la liste des initialiseurs base/membre du constructeur
// Screen.hpp
class Screen : public sf::Thread
{
public :
Screen();
int Run(sf::RenderWindow &window);
protected :
sf::RenderWindow &App;
int exit;
};
// Screen.cpp
Screen::Screen() : exit(-1)
{
}
int Screen::Run(sf::RenderWindow &window)
{
App = window;
Launch();
return exit;
}
J'ai tenté d'initialiser sf::RenderWindow App dans le constructeur en mettant: App(sf::RenderWindow()) mais ça ne marche pas.
-
Tu ne peux pas "binder" une référence après sa construction, c'est pourquoi j'ai utilisé un pointeur dans mon exemple.
-
En effet, j'avais oublié.
Je te répondrai plus en détail plus tard car il y a encore quelques problèmes etje n'ai pas le temps ce week-end.
-
Alors me revoilà.
Donc en fait il y a un problème de pointage, c'est-à-dire que lorsque je crée une classe Jeu qui hérite de Screen avec pour fonction virtual void Run(), l'attribut hérité de Screen (sf::RenderWindow *App) ne "marche" pas dans la fonction héritée.
Il y a comme un problème de pointage avec le sf::RenderWindow App(sf::VideoMode(1024, 768, 32), "Nouveau Jeu", sf::Style::Close) du main puisque par exemple lorsque j'appelle GetWidth() de sf::RenderWindow, aucune valeur n'est renvoyée ...
De plus, rien n'est affiché à l'écran.
-
aucune valeur n'est renvoyée
Ca veut dire quoi ? Ca crashe ? Ca renvoie zéro ? Du caca ?
Le code est-il suffisamment petit pour que tu puisses nous le montrer (au moins en partie) ?
-
Ça ne crash pas mais si je veux afficher la valeur avec cout :
cout << App->GetWidth() << endl;
Aucune valeur n'est affichée alors que si je place ce bout de code dans la fonction Run(sf::RenderWindow &App) qui est déclarée dans la classe Screen, ça affiche bien la longueur de la fenêtre (ici 1024)
Voilà un bout de code :
class Screen : public sf::Thread
{
public :
Screen();
int Run(sf::RenderWindow &window);
//int GetExit();
//virtual void Run();
protected :
sf::RenderWindow *App;
int exit;
//private :
//virtual void Run() = 0;
};
Screen::Screen() : exit(-3)
{
}
int Screen::Run(sf::RenderWindow &window)
{
App = &window;
Launch();
while(exit == -3);
return exit;
}
void Jeu::Run()
{
bool Running(true);
sf::Event Event;
while(Running)
{
while(App->GetEvent(Event))
{
if(Event.Type == sf::Event::Closed)
exit = SCREEN_EXIT;
if(Event.Type == sf::Event::KeyPressed)
{
switch(Event.Key.Code)
{
case sf::Key::Escape:
exit = SCREEN_MENU;
break;
}
}
}
if(exit != -3)
Running = false;
// Affichage
App->Clear(sf::Color(0, 255, 0));
App->Display();
}
exit = SCREEN_EXIT;
}
Et la fonction main :
int main(int argc, char** argv)
{
sf::RenderWindow App(sf::VideoMode(1024, 768, 32), "Nouveau Jeu", sf::Style::Close);//|sf::Style::Fullscreen);
//sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Nouveau Jeu", sf::Style::Close);
// Vue
sf::View view(sf::FloatRect(0, 0, 1024, 768));
App.SetView(view);
// Menu/Jeu/Options
std::vector<Screen*> screens;
int screen = 0;
Jeu s_jeu; // Creation d'un affichage pour le jeu
screens.push_back(&s_jeu);
while(screen >= 0)
{
screen = screens[screen]->Run(App);
}
return EXIT_SUCCESS;
}
Une remarque :
Par rapport à la ligne while(exit == -3);, ça ne fonctionne pas comme je veux donc ne la prend pas en compte, c'est juste pour ne pas répéter la boucle du main.
-
Donc en fait Jeu::Run() n'est pas du tout appelé ? C'est ça ?
-
Si si puisque un cout << "text" << endl; dans la fonction Run() affiche correctement le texte dans la console. C'est le pointeur sf::RenderWindow *App qui a un problème car le fait d'afficher un fond rouge
App->Clear(sf::Color(0, 255, 0));
App->Display();
ne marche pas puisqu'il y a un écran noir.
De plus, App->GetEvent(Event) renvoie toujours false car la boucle while(App->GetEvent(Event)) de la fonction Run() n'est jamais lancé. Donc j'en déduit qu'il y a un problème de pointage ... Et je ne comprends pas pourquoi ça ne marche pas
-
T'es sûr que tu n'as pas une autre instance de sf::RenderWindow, non initialisée, qui traîne quelque part et qui pourrait être pointée à la place de la bonne instance ?
-
Je suis sûr. En fait, ça vient du fait que la classe jeu hérite de Screen car dans la fonction Run(sf::RenderWindow &window) de Screen, si j’ajoute:
App->Clear(sf::Color(255, 0, 0));
App->Display();
La fenêtre devient rouge alors que ça ne marche pas dans la classe Jeu ...
-
Si jeu hérite de screen alors tu as 2 prototypes différents de run().
Il faut que la méthode run() de jeu est le même prototype que celui de screen non ?
int Run(sf::RenderWindow &window);
sinon la méthode run() de jeu n'est jamais appelée.
j'ai lu le thread en travers mais rejette un oeil au tuto du wiki
https://github.com/SFML/SFML/wiki/TutorialScreens (https://github.com/SFML/SFML/wiki/TutorialScreens)
et adapte en conséquences
-
Run(sf::RenderWindow&) n'est pas virtuelle, elle lance le thread qui lui va appeller Run().
-
Run(sf::RenderWindow&) n'est pas virtuelle, elle lance le thread qui lui va appeller Run().
Oui et j'avais testé avec un cout dans la fonction Run() de Jeu pour voir si elle était bien appelée, ce qui est le cas.
Je ne sais pu trop quoi faire pour résoudre le problème :-\
-
Je vais passer bientôt à la SFML 2.0, ça réglera peut-être le problème ?
-
Peut-être, peut-être pas ;)
Mais je ne pense pas, étant donné que ça sent quand même le problème dans ton code et non dans SFML.
-
Alors voilà, j'ai réussi à afficher le sf::RenderWindow mais avec un thread différent :
#include <SFML/Graphics.hpp>
#include "all_screens.hpp"
Engine::Engine(sf::RenderWindow &w) : App(w)
{
}
void Engine::Run()
{
App.Create(sf::VideoMode(1024, 768, 32), "Nouveau Jeu", sf::Style::Close);
// Vue
sf::View view(sf::FloatRect(0, 0, 1024, 768));
App.SetView(view);
// Menu/Jeu/Options
Menu s_menu; // Creation d'un menu
screens.push_back(&s_menu);
Jeu s_jeu; // Creation d'un affichage pour le jeu
screens.push_back(&s_jeu);
int screen(0);
while(screen >= 0)
{
cout << screen << endl;
screen = screens[screen]->Run(App);
}
}
int main(int argc, char** argv)
{
sf::RenderWindow Window;
Engine App(Window);
App.Launch();
sf::Clock c;
while(c.GetElapsedTime() < 20);
return EXIT_SUCCESS;
}
#ifndef HEADER_ALL_SCREENS
#define HEADER_ALL_SCREENS
#include "Screen.hpp"
// Screens :
#include "Menu.hpp"
#include "Jeu.hpp"
class Engine : public sf::Thread
{
public :
Engine(sf::RenderWindow &w);
virtual void Run();
private :
sf::RenderWindow &App;
std::vector<Screen*> screens;
};
#endif
Et la fonction Run de Menu et Jeu :
int Menu::Run(sf::RenderWindow &w)
{
sf::RenderWindow &App = w;
// ....
}
Donc tout s'affiche correctement, le seul problème est que ce que le problème initial (le sujet de la discussion) n'est pas résolu. Lorsque je clique, déplace la fenêtre, le programme s'arrête toujours et donc dans mon cas, les collisions (le plus important actuellement) sont ignorées.
-
Pas de réponse ?