Bonjour à tous !
Je developpe des petits programmes Windows depuis peu sous Dev C++ 5 (version 4.9.9.2 beta).
Jusqu'alors, je me contentais des simples WM_KEYDOWN et WM_KEYUP pour gérer le clavier, ce qui suffisait amplement à mes besoins.
Mais depuis peu, j'essaye de créer des mini-jeux 2D et il semblerait qu'il faille optimiser cette gestion du clavier.
Certes, il existe les fameux OpenGL et DirectX mais je préfèrerais utiliser l'API Win32, car je n'ai pas trop le temps de me pencher sur ces technologies.
Vous trouverez ci-dessous le code source complet de mon programme qui permet de déplacer un "pixel" dans une fenêtre Windows à l'aide des flèches du clavier.
Malheureusement, lorsqu'on appuie sur plusieurs touches en même temps (par exemple : flèche HAUT et flèche GAUCHE), le deplacement du "pixel" n'est pas FLUIDE.
Il y a un temps de réaction et les messages WM_KEYDOWN semblent être envoyés par Windows de façon bizarre...
Je ne comprends pas bien pourquoi mon "pixel" ne se déplace pas normalement et/ou s'immobilise !!!
Quelqu'un pourrait-il m'aider ? Ca serait vraiment sympa !
Voici le code source en C (il n'y a que 3 petites fonctions : l'incontournable WinMain, WindowProcedure pour la gestion des évènements et afficherPixel) :
#include <windows.h>
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
char szClassName[ ] = "Windows App";
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof(WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default color as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx(&wincl))
return 0;
/* The class is registered, let's create the program */
hwnd = CreateWindowEx(
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Test clavier", /* Title Text */
WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX|WS_VISIBLE,
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
406, /* The programs width */
425, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow(hwnd, nFunsterStil);
/* Run the message loop. It will run until GetMessage() returns 0 */
while(GetMessage(&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
// VARIABLE GLOBALE :
// La position courante du pixel dans la fenetre, initialement au centre
POINT position = {200, 200};
// Declaration de la fonction d'affichage du pixel a la position courante
void afficherPixel(HDC hdc);
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
// Gestion simultanee des touches du clavier (BUG WINDOWS ???) :
// Deplacer la position courante du pixel puis afficher le contenu de la fenetre
case WM_KEYDOWN:
{
short bitMaskKeyPressed = 32768;
// La touche HAUT est enfoncee
if(GetKeyState(VK_UP) & bitMaskKeyPressed)
position.y -= 1;
// La touche BAS est enfoncee
if(GetKeyState(VK_DOWN) & bitMaskKeyPressed)
position.y += 1;
// La touche DROITE est enfoncee
if(GetKeyState(VK_RIGHT) & bitMaskKeyPressed)
position.x += 1;
// La touche GAUCHE est enfoncee
if(GetKeyState(VK_LEFT) & bitMaskKeyPressed)
position.x -= 1;
}
/* NE FONCTIONNE PAS DU TOUT (Gestion simultannnee des touches du clavier)
switch(wParam)
{
// Touche HAUT
case VK_UP :
position.y -= 1;
break;
// Touche BAS
case VK_DOWN :
position.y += 1;
break;
// Touche DROITE
case VK_RIGHT :
position.x += 1;
break;
// Touche GAUCHE
case VK_LEFT :
position.x -= 1;
break;
}
*/
// Afficher le contenu de la fenetre
HDC hdc=GetDC(hwnd);
afficherPixel(hdc);
ReleaseDC(hwnd, hdc);
return 0;
// Quitter le programme
case WM_DESTROY:
PostQuitMessage(0);
break;
// Afficher le contenu de la fenetre quand c'est necessaire
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc=BeginPaint(hwnd, &ps);
afficherPixel(hdc);
EndPaint(hwnd, &ps);
}
return 0;
// Laisser Windows gerer par defaut les autres evenements
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
// Fonction d'affichage du pixel a la position courante
void afficherPixel(HDC hdc)
{
HDC hdcMem = CreateCompatibleDC(NULL);
HBITMAP hBmpMem = CreateCompatibleBitmap(hdc, 400, 400);
HBITMAP hOldBmp = SelectObject(hdcMem, hBmpMem);
RECT windowRect = {0, 0, 400, 400};
RECT pixelRect = {position.x - 5, position.y - 5, position.x + 10, position.y + 10};
// Effacer le contenu de la fenetre (en rouge)
FillRect(hdcMem, &windowRect, CreateSolidBrush(255));
// Dessiner le pixel a la position courante (un carre de 10x10 en noir)
FillRect(hdcMem, &pixelRect, CreateSolidBrush(0));
BitBlt(hdc, 0, 0, 400, 400, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hOldBmp);
DeleteObject(hBmpMem);
DeleteObject(hOldBmp);
DeleteDC(hdcMem);
}