Jump to content
  • Advertisement
Sign in to follow this  
boogyman19946

weird win32 GDI bug

This topic is 3004 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Alright, I've been happily restoring my programming on the pong game I've started some week ago and I am struggling with a weird and relatively hard to find bug.

Here's the what's happening: When I start the game, the two paddles on both the left and the right sides render properly. The game is in a paused state at first so everything is drawn only once. As soon as I unpause the game, the paddles are only drawn at about half of what the length is supposed to be. This brings me to the conclusion that the first time the objects render, they are fine, but after that one drawing cycle, something prevents them from being drawn properly. I have no idea what's going on.

Here's the code I'm working with:


// Main.cpp
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
///////////////////
// PREPARATION //
///////////////////

backBuffer.ClearBuffer();

// Background
hbmpOld = (HBITMAP)SelectObject(hMemDC, hBackgrBmp);

BitBlt(hMainDC, 0, 0, backBuffer.mWidth, backBuffer.mHeight, hMemDC, 0, 0, SRCCOPY);

//////////////////
// MOVE OBJECTS //
//////////////////

if (!gPause) {
PlayerEnum pe;

gpPlayerTwo->MoveTowards(gpBall->mPosition); // non human player moves towards ball

pe = gpBall->MoveBall(*gpPlayerOne, *gpPlayerTwo);
if(pe != NONE)
{
if (pe == PLAYER1)
{
gpPlayerOne->mScore++;
TextOut(hMainDC, 400, 300, "Player 1 scored!", 17);
} else if (pe == PLAYER2) {
gpPlayerTwo->mScore++;
TextOut(hMainDC, 400, 300, "Player 2 scored!", 17);
}

backBuffer.PresentBuffer();

//gpPlayerOne->CenterPlayer(center);
//gpPlayerTwo->CenterPlayer(center);
gpBall->ResetBall();
Sleep(2000);
}
}

/////////////
// DRAWING //
/////////////

// Draw objects
gpBall->Draw(hMainDC, hMemDC);
gpPlayerOne->Draw(hMainDC, hMemDC);
gpPlayerTwo->Draw(hMainDC, hMemDC);

std::string tempString = "Player 1's score: ";
tempString += itos(gpPlayerOne->mScore);
TextOut(hMainDC, 260, 50, tempString.c_str(), tempString.length());
tempString.erase();

tempString = "Player 2's score: ";
tempString += itos(gpPlayerTwo->mScore);
TextOut(hMainDC, 410, 50, tempString.c_str(), tempString.length());
tempString.erase();

if (gPause)
TextOut(hMainDC, 400, 300, "Paused!", 7);

backBuffer.PresentBuffer();

//////////////
// CLEAN UP //
//////////////

SelectObject(hMemDC, hbmpOld);

Sleep(20);
}
}






//Player.cpp
void Player::MovePlayer(signed int argYPos) {
POINT tempYPos;
tempYPos.x = mPosition.x;
tempYPos.y = mPosition.y - argYPos;

if (OutOfBounds(tempYPos, mBounds)) {
// Test top and bottom sides separately to reposition the paddle
if (tempYPos.y < mBounds.top) {
tempYPos.y = mBounds.top; // Set it right by the bound
} else if (tempYPos.y > mBounds.bottom) {
tempYPos.y = mBounds.bottom;
}
}

mPosition.y = tempYPos.y;
}

void Player::Draw(HDC hBBDC, HDC hMemDC) {
HBITMAP hbmpOld;
hbmpOld = (HBITMAP)SelectObject(hMemDC, mhbmpPaddle);

BitBlt(hBBDC, mPosition.x, mPosition.y, mWidth, mHeight, hMemDC, 0, 0, SRCPAINT);

SelectObject(hMemDC, hbmpOld);
}






I only posted the pieces of code I believe to be relevant. I didn't post the header files because I don't think they are necessary. Also, I'm quite certain that in Player::Draw(HDC, HDC) the variables mWidth and mHeight stay intact because I changed the values into literals and I got the same bug as before.

Can anyone help me out?

Source code

[Edited by - boogyman19946 on July 1, 2010 5:07:23 PM]

Share this post


Link to post
Share on other sites
Advertisement
Ok I posted a response, but It seems Im not understand exactly whats going on with your program.

There is a create/destroy DC in each frame - id try to optimize that out for a start.
You can keep all your DCs and bitmaps that you create around. The only one you need to release is the window's own (from GetDC())

Also you have a call to Delete the oldbitmap that was returned from SelectObject,. I dont think you should do that. You are supposed to select it back into the DC before deleting the DC


Share this post


Link to post
Share on other sites
empirical, I actually know that. The code used to be the way you suggest it but I moved it into the game loop to see if the bug would go away and I forgot to place it back. Thanks for the advice though. Everything you described is already in place. The rendering to window's DC happens in PresentBuffer() function of the BackBuffer.

Any other suggestions?

Share this post


Link to post
Share on other sites
Damn ninja edit /fail. :D

Im not sure. If no one else has solved when I get back in a couple of hours, ill stdy it then.

Do the paddles move around? if so do they maintain this half length regardless of what position they are in?

Share this post


Link to post
Share on other sites
Quote:
Original post by boogyman19946
Alright, I've been happily restoring my programming on the pong game I've started some week ago and I am struggling with a weird and relatively hard to find bug.

Can anyone help me out?


Hello Boogyman the way you try to perfom drawing with GDI is very wrong
If you want to use GDI and paint objects on the screen, you should consider using WM_PAINT message instead.
With the code given above what you are doing in fact is creating a bottleneck in the message processing


Quote:

/// the main function entry point
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
MSG msg ;
WNDCLASS wndclass ;
wndclass.lpfnWndProc = WndProc ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Can't Create Window!"),
g_szAppName, MB_ICONERROR) ;
return 0 ;
}
////// window initialization /////
//....
////// window message dispatching /////

while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}



/////// Window Procedure function /////

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HDC hdc;
static PAINTSTRUCT ps;
switch (message)
{

case WM_SIZE:
break;
case WM_PAINT:
hdc=BeginPaint(&ps);
/////////////////// use the HDC and make all drawing here
Paint_Objects(hdc);
ReleaseDC(hdc);

}
break;

return DefWindowProc(hWnd, message, wParam, lParam);
}

void Paint_Objects(HDC hdc)
{
HDC hMainDC = hdc;
HDC hMemDC = CreateCompatibleDC(hMainDC);
backBuffer.ClearBuffer();

//////////////////
// MOVE OBJECTS //
//////////////////

if (!gPause) {
PlayerEnum pe;

gpPlayerTwo->MoveTowards(gpBall->mPosition); // non human player moves towards ball

pe = gpBall->MoveBall(*gpPlayerOne, *gpPlayerTwo);
if(pe != NONE)
{
if (pe == PLAYER1)
{
gpPlayerOne->mScore++;
TextOut(hMainDC, 400, 300, "Player 1 scored!", 17);
} else if (pe == PLAYER2) {
gpPlayerTwo->mScore++;
TextOut(hMainDC, 400, 300, "Player 2 scored!", 17);
}

//gpPlayerOne->CenterPlayer(center);
//gpPlayerTwo->CenterPlayer(center);
gpBall->ResetBall();
Sleep(2000);
}
}

/////////////
// DRAWING //
/////////////

// Background
HBITMAP hbmpOld = (HBITMAP)SelectObject(hMemDC, hBackgrBmp);

BitBlt(hMainDC, 0, 0, backBuffer.mWidth, backBuffer.mHeight, hMemDC, 0, 0, SRCCOPY);

// Draw objects
gpBall->Draw(hMainDC, hMemDC);
gpPlayerOne->Draw(hMainDC, hMemDC);
gpPlayerTwo->Draw(hMainDC, hMemDC);

std::string tempString = "Player 1's score: ";
tempString += itos(gpPlayerOne->mScore);
TextOut(hMainDC, 260, 50, tempString.c_str(), tempString.length());
tempString.erase();

tempString = "Player 2's score: ";
tempString += itos(gpPlayerTwo->mScore);
TextOut(hMainDC, 410, 50, tempString.c_str(), tempString.length());
tempString.erase();

if (gPause)
TextOut(hMainDC, 400, 300, "Paused!", 7);

backBuffer.PresentBuffer();

//////////////
// CLEAN UP //
//////////////

SelectObject(hMemDC, hbmpOld);

DeleteDC(hMemDC);
DeleteObject(hbmpOld);

Sleep(20);
}
}
}



I should recommend to create a graphic class managing back-buffer HDC and all other stuff

Share this post


Link to post
Share on other sites
@empirical, yup, the paddles do move. The right one moves towards the altitude of the ball and the left one moves accordingly with the mouse (only vertical that is) and the problem affects both paddles. The only time they are how intended is in the very beginning when the first frame is drawn and no changes are made.

@Holy Grail, I don't understand what exactly do you mean? Why does it create a bottle neck? And how can I put motion picture in a WM_PAINT message? From what I understand, messages are dispatched when an event happens and with GetMessage() when there are no messages, the program halts/stalls. This is counter productive considering a game with moving graphics where things need to be repainted regardless of input. And I'm guessing that putting that break statement outside of a switch branch was simply a typo on your part. I really don't mean to be rude, but your code won't work for what I'm trying to do.

EDIT: I got the code back to the way it's supposed to be in the first place. I've edited the source in the first post.

[Edited by - boogyman19946 on July 1, 2010 12:45:59 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by boogyman19946
@Holy Grail, I don't understand what exactly do you mean? Why does it create a bottle neck?


I don't see a bottle neck because if you receive a message you do not then proceed to frame. Which is correct, the queue is purged before the program continues.

Also, no, putting your per-frame code in response to WM_PAINT makes no sense. Unless you demand windows update every frame or something. Would be very messy.

I cant see anything obvious with that sorry, but its only a fragment.

I would suggest the following, create a separate DC for the paddles and keep it. Then you don't need to select the bitmaps everytime you want to draw. I doubt this will fix anything but its best practice and *might* be the problem.

Even better (as holygrail said) is to create a image class which has one DC, one Bitmap and is self managing. Give it a HDC operator and you can use your new image object in BitBlit operations tidying up all the SelectObject/CreateDC/DestroyDC.

Short of separating the DCs out I would need to have the full source to run it and investigate further.

Share this post


Link to post
Share on other sites
@empirical, Yay :D I got it working. I gave Player it's own memory DC, selected the paddle bitmap into it, and left it like that for drawing, now the everything is rendering fine. I'm guessing it was a problem with the memory DC but I still don't understand why that happened. I'm going to post the full source code in a minute. I'd like to have this figured out so I can avoid the problem in the future. I don't like problems just magically appearing out of nowhere >.>

EDIT: Ok this is taken way longer than I expected >.> I'll upload it soon enough though, still have to find a place to put the source code.

EDIT2: Ok done, I'm adding the media fire link at the bottom of the original post right now

[Edited by - boogyman19946 on July 1, 2010 2:48:17 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!