OVH Cloud OVH Cloud

Problème d'affichage (VC++)

4 réponses
Avatar
ByB
Bonjour,

Je suis en train de développer un jeu d'Othello en VC++ avec les MFC.

J'ai créé un tableau de jeu avec un CDialog qui comporte 64 boutons,
que j'ai transformé en FooButtons
(http://www.codeproject.com/buttonctrl/FooButton.asp) afin de pouvoir
facilement afficher des images sur ces boutons : l'image d'une case
vide, d'une case avec un pion blanc, ou d'une case avec un pion noir
(grace à la méthode m_fooButton.setBitmapId (IDB_FOO_BUTTON))

J'ai créé une commande OnButtonRange() pour gérer le clic sur les 64
boutons (jeu du joueur humain). Cette commande se termine par une
commande reDraw() qui permet d'associer les images correspondant à
l'état des boutons (case vide, case blanche ou case noire), puis
appelle la méthode CPUPlay() qui permet à l'ordinateur de jouer à son
tour.
En gros, ça donne ça :

CJeu::OnButtonRange(int nID)
{
//.....
// Gestion du clic

jeu->reDraw(); // Redessine le jeu

CPUPlay(); // Appel de la méthode pour que l'ordinateur joue
}


CJeu::CPUPLay()
{
//...
//Gestion du jeu de l'ordinateur
// ...

jeu->reDraw(); /// Redessine le jeu

}

Or je constate que seule la dernière méthode reDraw() (celle appelée
depuis CPUPlay()), et qui laisse le jeu "en attente" d'un nouveau clic,
affiche correctement le tableau de jeu. En clair, l'appel de reDraw()
dans OnButtonRange(), qui est tout de suite suivi de l'appel de
CPUPlay() , ne permet pas de voir les changements, même si je met un
Sleep(1000) pour faire une tempo.Ce reDraw() passe en fait inaperçu, et
pourrait aussi bien ne pas être dans le programme.

Ma question est donc : Comment faire pour que le reDraw() qui suit la
gestion du clic (dans OnButtonRange()) ait un effet, et pour que
l'utilisateur du programme voit l'effet de son clic avant de voir le
résultat du jeu de l'ordinateur (second appel à reDraw()) ?
Y a t-il quelque chose à faire pour que le CDialog se mette à jour
après le premier reDraw() ? Un message à envoyer ?

Je sais que mon message n'est pas très clair, mais je remercie ceux qui
pourront me conseiller sur ce point ...







--
"Heureusement que Jésus-Christ n'est pas mort dans son lit. Sinon, en
Bretagne, il y aurait un sommier en granit à chaque carrefour."
(Jean Yanne / 1933-2003 / Je suis un être exquis)

4 réponses

Avatar
ByB
Personne d'autre que ByB n'aurait pu écrire que

Ma question est donc : Comment faire pour que le reDraw() qui suit la gestion
du clic (dans OnButtonRange()) ait un effet, et pour que l'utilisateur du
programme voit l'effet de son clic avant de voir le résultat du jeu de
l'ordinateur (second appel à reDraw()) ?
Y a t-il quelque chose à faire pour que le CDialog se mette à jour après le
premier reDraw() ? Un message à envoyer ?



Il y a bien une solution (qui n'est pas satisfaisante) et c'est
d'afficher un MessageBox() juste après le premier reDraw(), mais je ne
peux pas demander à l'utilisateur de valider un MessageBox() après
chaque clic ...


--
N'est-ce pas étrange que les ordinateurs fassent aujourd'hui des choses
jugées inutiles il y a vingt ans ? [Gene Perret]
Avatar
Arnaud Debaene
"ByB" a écrit dans le message de news:

Bonjour,

Je suis en train de développer un jeu d'Othello en VC++ avec les MFC.

J'ai créé un tableau de jeu avec un CDialog qui comporte 64 boutons, que
j'ai transformé en FooButtons


<snip>
J'ai créé une commande OnButtonRange() pour gérer le clic sur les 64
boutons (jeu du joueur humain). Cette commande se termine par une commande
reDraw() qui permet d'associer les images correspondant à l'état des
boutons (case vide, case blanche ou case noire), puis appelle la méthode
CPUPlay() qui permet à l'ordinateur de jouer à son tour.
En gros, ça donne ça :

CJeu::OnButtonRange(int nID)
{
//.....
// Gestion du clic

jeu->reDraw(); // Redessine le jeu

CPUPlay(); // Appel de la méthode pour que l'ordinateur joue
}


CJeu::CPUPLay()
{
//...
//Gestion du jeu de l'ordinateur
// ...

jeu->reDraw(); /// Redessine le jeu

}

Or je constate que seule la dernière méthode reDraw() (celle appelée
depuis CPUPlay()), et qui laisse le jeu "en attente" d'un nouveau clic,
affiche correctement le tableau de jeu. En clair, l'appel de reDraw() dans
OnButtonRange(), qui est tout de suite suivi de l'appel de CPUPlay() , ne
permet pas de voir les changements, même si je met un Sleep(1000) pour
faire une tempo.Ce reDraw() passe en fait inaperçu, et pourrait aussi bien
ne pas être dans le programme.



Normal : Quantd tu fais un redraw, tu postes indirectement un message
WM_PAINT à ta fenêtre. Ce message ne sera traité que quand tu retourneras
dans la pompe à messages de ton application (pour mémoire, les messages
postés à une fenêtres sont traîtés 1à 1, 1 à chaque fois que l'on passe dans
la pompe à messages). Tu fais l'erreur classique de vouloir faire un
traîtement long et bloquant (CPUPlay) depuis un handler d'événement du
programme (OnButtonRange). En faisant cela, tu empêche le thread de GUI
d'actionner la pompe à messages et donc de traîter les messages à
destination de ta fenêter (y compris les ordres de redessin).

Il y a basiquement deux solutions :

- Faire tourner CPUPlay dans un thread ouvrier. Dans ton cas , c'est sans
doute se compliquer la vie pour rien puisqu'il faudrait synchroniser ce
thread avec l'IHM pour faire tourner CPUPlay au bon moment.

- Au lieu d'appeler directement CPUPlay depuis OnButtonRange, postes-toi à
toi même un message personnalisé qui provoque l'appel à CPUPlay. Quleque
chose du genre :

static const int WM_CPUTURN = WM_USER+1;
CJeu::OnButtonRange(int nID)
{
//.....
// Gestion du clic
jeu->reDraw(); // Postes un WM_PAINT
this->PostMessage(WM_CPUTURN); // Puis postes un appel à CPUPlay
}

CJeu::OnCpuTurn()
{
CPUPlay();
}

Arnaud
MVP - VC
Avatar
ByB
Dans son message précédent, Arnaud Debaene a écrit :

Il y a basiquement deux solutions :

- Faire tourner CPUPlay dans un thread ouvrier. Dans ton cas , c'est sans
doute se compliquer la vie pour rien puisqu'il faudrait synchroniser ce
thread avec l'IHM pour faire tourner CPUPlay au bon moment.

- Au lieu d'appeler directement CPUPlay depuis OnButtonRange, postes-toi à
toi même un message personnalisé qui provoque l'appel à CPUPlay. Quleque
chose du genre :

static const int WM_CPUTURN = WM_USER+1;
CJeu::OnButtonRange(int nID)
{
//.....
// Gestion du clic
jeu->reDraw(); // Postes un WM_PAINT
this->PostMessage(WM_CPUTURN); // Puis postes un appel à CPUPlay
}

CJeu::OnCpuTurn()
{
CPUPlay();
}

Arnaud
MVP - VC




Merci pour cette réponse rapide. Je vais mettre en place la seconde
solution, qui me parait être la plus raisonnable.




--
L'homme est imparfait mais ce n'est pas étonnant si l'on songe à
l'époque où il fut créé.
[Alphonse Allais]
Avatar
ByB
Si je n'étais pas venu sur ce forum le 03/07/2006, aurai-je su que ByB
avait dit que
Dans son message précédent, Arnaud Debaene a écrit :

Il y a basiquement deux solutions :

- Faire tourner CPUPlay dans un thread ouvrier. Dans ton cas , c'est sans
doute se compliquer la vie pour rien puisqu'il faudrait synchroniser ce
thread avec l'IHM pour faire tourner CPUPlay au bon moment.

- Au lieu d'appeler directement CPUPlay depuis OnButtonRange, postes-toi à
toi même un message personnalisé qui provoque l'appel à CPUPlay. Quleque
chose du genre :

static const int WM_CPUTURN = WM_USER+1;
CJeu::OnButtonRange(int nID)
{
//.....
// Gestion du clic
jeu->reDraw(); // Postes un WM_PAINT
this->PostMessage(WM_CPUTURN); // Puis postes un appel à CPUPlay
}

CJeu::OnCpuTurn()
{
CPUPlay();
}

Arnaud
MVP - VC




Merci pour cette réponse rapide. Je vais mettre en place la seconde solution,
qui me parait être la plus raisonnable.



Je dois juste rajouter que d'après mes tests, il est nécessaire, pour
que l'affichage soit correct, d'ajouter un appel à UpdateWindow() avant
d'envoyer le message.

Dans mon CDialog, le code est maintenant :

// SNIP code avant
if (rep > 0)
{
UpdateWindow();
Sleep(5000);

ChangePlayer();
SendMessage(WM_APP+42,0,0);
}

///

L'UpdateWindow() permet le redessin du CDialog, le Sleep(5000) donne 5
secondes à l'utilisateur pour voir le résultat de son clic, j'appelle
la fonction ChangePlayer() (une fonction à moi) pour quelques
initialisations, et puis je poste le message que j'ai défini. Et ça
marche.

Merci.





--
C'est drôle, on parle souvent du Pôle Nord, plus rarement du Pôle Sud,
et jamais du Pôle Ouest ni du Pôle Est. Pourquoi cette injustice ?
...ou cet oubli ?
[Alphonse Allais]