Wait function combines with simple graphics.

For my final project, I have created a memory match game. It is a simple visual studio project. I have completed all the code, yet I have this one last problem I cannot seem to correct.

My Problem: Since it is a memory matching game, the user clicks on squares that reveal what is under them. If they do not match, they need to unreveal themselves. The problem is - that when they click on the second square, the tiles immediately compare and unreveal themselves. I need the user to be able to look at the secons square for a few seconds so they can remember what is on it. I found a simple wait function:

void wait(int seconds) {
clock_t endwait;
endwait = clock () + seconds * CLOCKS_PER_SEC ;
while (clock() < endwait) {}
}

case WM_LBUTTONUP:
//Get the coordinates to the array
xPos = (LOWORD(lParam) - 100) / 100;
yPos = (HIWORD(lParam) - 100) / 100;

//Tell the game manager to add a move
hdc = BeginPaint(hWnd, &ps);
game.SetMove( xPos, yPos, hdc, hWnd);
EndPaint(hWnd, &ps);
wait(1);
//Force a paint message of the gameboard
InvalidateRect(hWnd, NULL, true);

break;

I implement it after I call game.SetMove, which is when my code draws the image assigned to that certain square. However, when I run it and click a square, the waiting period occurs before my image is drawn! I have placed the wait function everywhere throughout my code and it always waits before it draws my image. Therefor, my second image still never shows up! How is that possible?
by putting yourself in a while() loop like that, you're deadlocking your program, which doesn't allow the redrawing code to run. Even if you called a Sleep function (which would be preferable to deadlocking because it doesn't hog CPU time) you still are never getting to the code that draws the function.

You need to have your code come back to the message pump so that is can process paint events and redraw the window. This means you can't have a blocking call -- you must exit right away.

What you can do, is set up a timer. I forget the exact syntax but it's probably something like:

1
2
3
4
5
6
7
8
// when you want to wait for X milliseconds
timerid = SetTimer( hwnd, number_of_milliseconds, 0 );

// then in your message handler:
case WM_TIMER:
  KillTimer(hwnd,timerid);
  DoWhateverAfterTimeIsUp();
  break;


Look up SetTimer/KillTimer/WM_TIMER for more details.
Last edited on
OK, I still do not really understand why a simple wait function will not work. However, with WM_SetTimer, I do not undersatnd how to actually call the function. I do not understand when a Timer message is actually sent.
Upon closer inspection of your original code... I find my previous answer was a bit incomplete. Sorry.

1
2
3
4
//Tell the game manager to add a move
hdc = BeginPaint(hWnd, &ps);
game.SetMove( xPos, yPos, hdc, hWnd);
EndPaint(hWnd, &ps);


BeginPaint/EndPaint pairs only allow you to draw to the area of the screen that has been Invalidated (with InvalidateRect). If you don't invalidate the screen, the drawing here does nothing. This is why you were not seeing onscreen changes.

A "fix" (really, a hack) is to use GetDC/ReleaseDC instead of BeginPaint/EndPaint. Or Invalidate the desired rect(s) before calling BeginPaint.

-----------

That said -- I stand by my original post in that the layout you're going for here is fundamentally flawed. Sleeping (or worse... deadlocking) for any period of time makes your program completely unresponsive for that period of time. For only 1 second it might not be so bad, but for longer time periods you're just asking for trouble.

If you really want to use this wait method (I understand the appeal -- it certainly is simpler than setting up a timer), then at least use Sleep() instead of that custom wait() function:

 
Sleep(1000);  // wait for 1 second 


Sleep tells the OS that your program does not need CPU time, which allows other programs to take CPU time. Whereas deadlocking in a while() loop makes your program chew up 100% of the CPU time reducing overall system performance.

As for how to use SetTimer -- look it up. http://msdn.microsoft.com -- search for SetTimer, KillTimer, or WM_TIMER (read: not WM_SetTimer). I'm sure there are examples.
Last edited on
Ok, I am kind of following you. Thankyou for your responses. I just stepped through my code with the debugger and the code that draws my images is definitely being called, but not outputting to the screen.

It calls the sleep function exactly when I want to, but since the second image does not draw, my program still does not work right.

I tried the GetDC and the ReleaseDC, but they seemed to be some sort of member objects and I could not figure out how to use them.

It seems as if this could be a very quick fix, yet I am just missing a key concept.

Sorry I am so dull :(
I tried the GetDC and the ReleaseDC, but they seemed to be some sort of member objects and I could not figure out how to use them.


Well I don't know how game.SetMove works, but I'm assuming it just draws to the given HDC (though why does it need a HWND?)

here's what you're doing with BeginPaint/EndPaint:
1
2
3
hdc = BeginPaint(hWnd, &ps);
game.SetMove( xPos, yPos, hdc, hWnd);
EndPaint(hWnd, &ps);


here's how to do the exact same thing with GetDC/ReleaseDC:
1
2
3
hdc = GetDC(hWnd);
game.SetMove( xPos, yPos, hdc, hWnd);
ReleaseDC(hWnd,hdc);
That did it! I am still not exactly sure what that changes. But I will look into it. Thanks so much Disch! You magically cured my headache as well :)
That did it! I am still not exactly sure what that changes


Windows allows drawing to be sped up/optimized by "clipping" what you draw so that only things that are "dirty" get redrawn.

BeginPaint/EndPaint apply this clipping to the DC, and "skip over" stuff that's drawn outside of the areas marked as "dirty". This allows dumb drawing code which redraws the entiere screen to be very fast, because drawing that is unnecessary is skipped. To get BeginPaint/EndPaint to actually DO anything, you must mark areas of the screen as "dirty". This is done by calling InvalidateRect().

GetDC/ReleaseDC are not smart that way. They don't care about any of the clipping, or what is "dirty" or not. They will simply draw whatever you draw to them even if it's redundant and unnecessary.

----

So your problem was that you were using BeginPaint/EndPaint, but not marking anything as dirty. One solution could have been to simply mark the card you're changing as dirty by calling InvalidateRect. The other solution is to just ignore the clipping and use GetDC/ReleaseDC.

Hope that clears it up.
Yea, that does. I had no idea about making objects "dirty" I had barrowed some code from one of my professor's slides without fully understanding it. You have been a huge help, and I now understand what's going on a lot better. I really appreciate it!
Topic archived. No new replies allowed.