Archived

This topic is now archived and is closed to further replies.

JIMbond21

Windows GDI question???

Recommended Posts

How do I eleminate the tearing effect when graphics are drawn using windows GDI? I use double buffering and my timer which makes the game run at 60 fps, and every 4 - 5 seconds it does the tearing effect. Plz Help!!!

Share this post


Link to post
Share on other sites
I use a bitmap in memory for the double buffer, then after drawing everything I blit that to the device context for the window, which I sort of make full screen by creating a maximized popup window, that appears as fullscreen. I only use GDI for rendering.

Share this post


Link to post
Share on other sites
No, there is no easy way. Detecting the retrace is low level stuff that Windows was designed to stop programs from performing. DirectX is the answer (until Longhorn arrives).

Jason Doucette | Matthew Doucette
Programming Windows, 5th Edition by Charles Petzold, Errata Addendum
projects / games | real-time graphics | artificial intelligence | world records | wallpapers / desktops / backgrounds
"Great minds discuss ideas, average minds discuss events, small minds discuss people." - Anna Eleanor Roosevelt, 1884-1962

Share this post


Link to post
Share on other sites
<< Any other way without any directx? >>

I use GDI only for my full screen Asteroids game and had no tearing at all. Haven''t worked with Windows Bitmaps yet but I expect no tearing. Put a link to your source...

Are you sure you are doing this each frame: (1) erase back buffer, (2) draw all graphics to back buffer, then when all drawing done (3) BitBlt entire back to front at once. If so, you shouldn''t see any tearing...

VazGames.com

Phil P

Share this post


Link to post
Share on other sites
He is speaking about tearing, not flickering. Tearing is when you are using double buffering, but are not updating the screen buffer in synch with the retrace, so you momentarily see one half of both frames (for only one retrace, until it happens again). Because of the nature of the retrace, it is most noticeable during horizontal movement - the faster, the more noticeable. Flickering, on the other hand, is when you erase and redraw. This is what will happen if he has a background brush set and is erasing the background every time. It causes an on then off effect, like a flickering light.

Jason Doucette | Matthew Doucette
Programming Windows, 5th Edition by Charles Petzold, Errata Addendum
projects / games | real-time graphics | artificial intelligence | world records | wallpapers / desktops / backgrounds
"Great minds discuss ideas, average minds discuss events, small minds discuss people." - Anna Eleanor Roosevelt, 1884-1962

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
an easy way to get rid of most tearing-related artifacts for GDI drawing is to not blit the entire back buffer in one go. instead, only blit those rectangles which have been updated since the last redraw - the dirty rectangle concept. as long as those rectangles are small, i.e., sprite-related, most of the time you will not notice the update. on the other hand, if you have lots of sprites moving about, it can get a bit complicated to figure out what rects need to be updated, and it also can cause the updates to get out of sync with each other on slow boxes. so, if you can''t find a way around your problem using standard old-school GDI drawing optimizations, then try taking a look at the article at http://www.codeproject.com/gdi/TearingFreeDrawing.asp . iirc, the author used DDraw to gain access to the vsync, but the actual drawing was done via GDI.

Share this post


Link to post
Share on other sites
Some one wanted the source code, this is for my game, it's still under development, as you can see.


// Name: Ultra Pong

// Description: A pong clone where the player hits a ball trying to keep the ball from missing the paddle.

// Author: John Petersen

// Version: 1.0


// Standard windows header file

#include <windows.h>
// Standard library

#include <stdlib.h>

void UpdateBuffer(); // Updates the double buffer

void ClearBuffer(); // Clears double buffer

void DisplaySplashScreen(); // Displays splash screen

int GameInit(); // Initialization

int GameLoop(); // Main game loop

int GameShutdown(); // Shutdown

int GetStringLength( char* String ); // Gets the length of a string

// Draws text

void DrawText( HDC hdc, char* szText, char* szFont, int Size, COLORREF crColor, bool bBold, bool bItalic, bool bUnderline, int x, int y);
// Draws bitmaps

void DrawBitmap(HDC hdc, HBITMAP hbmBitmap, HBITMAP hbmMask, int PosX, int PosY);
// Creates a bitmap mask

HBITMAP CreateBitmapMask( HBITMAP hbmBitmap, COLORREF crTransparent );

const char GameTitle[] = "Ultra Pong"; // Constant string to the game's title

HWND GameWindow = NULL; // Handle to the game's window

HINSTANCE GameInstance; // Handle to the game's instance

HDC g_hDC; // Global HDC

RECT g_rcClientRect; // Stores the RECT of GamwWindow

HDC g_hdcBuffer; // DC to the double buffer

HBITMAP g_hbmBuffer = NULL; // HBITMAP that is copied from the double buffer to the global DC

HBITMAP g_hbmOldBuffer = NULL; // Stores the old HBITAMP that was in the Buffer DC

bool g_GameDone = false; // BOOL that is true if the game is done

HBITMAP g_hbmBall = NULL; // HBITMAP for the ball

HBITMAP g_hbmBallMask = NULL; // HBITMAP for the ball mask

HBITMAP g_hbmPaddle = NULL; // HBITMAP for the paddle

HBITMAP g_hbmPaddleMask = NULL; // HBITMAP for the paddle mask

HBITMAP g_hbmSplashScreen = NULL; // HBITMAP for the splash screen

HBITMAP g_hbmWinScreen = NULL; // HBITMAP for the win screen

HBITMAP g_hbmLoseScreen = NULL; // HBITMAP for the lose screen

int g_Score = 0; // Score for game

char g_ScoreString[20]; // String for score

int g_BallsLeft = 5; // Number of balls left

char g_BallsLeftString[10]; // String for balls left



LONGLONG lastcount;
LONGLONG newcount;
LONGLONG ticknum;
LONGLONG tickdiff = 0;
int fps = 60;


int round(double x)
{
return int(x + 0.5);
}


typedef struct _BALL
{
int x, y;
int width, height;
int speedx, speedy;
}BALL;

typedef struct _PADDLE
{
int x, y;
int width, height;
int speedx, speedy;
}PADDLE;

BALL g_Ball;
PADDLE g_Paddle;

// Update double buffer

void UpdateBuffer()
{
BitBlt(g_hDC, 0, 0, g_rcClientRect.right, g_rcClientRect.bottom, g_hdcBuffer, 0, 0, SRCCOPY);
}

// Clears double buffer

void ClearBuffer()
{
FillRect(g_hdcBuffer, &g_rcClientRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
}

// Gets the length of a string

int GetStringLength( char* String )
{
int Length;

for(Length = 0; *String; String++, Length++);

return Length;
}

// Creates a bitmap mask

HBITMAP CreateBitmapMask( HBITMAP hbmBitmap, COLORREF crTransparent )
{
HDC hdcMem, hdcMem2;
HBITMAP hbmMask;
BITMAP bitmap;
GetObject(hbmBitmap, sizeof(BITMAP), &bitmap);
hbmMask = CreateBitmap(bitmap.bmWidth, bitmap.bmHeight, 1, 1, NULL);
hdcMem = CreateCompatibleDC(0);
hdcMem2 = CreateCompatibleDC(0);
SelectObject(hdcMem, hbmBitmap);
SelectObject(hdcMem2, hbmMask);
COLORREF crOld = SetBkColor(hdcMem, crTransparent);
BitBlt(hdcMem2, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
BitBlt(hdcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem2, 0, 0, SRCINVERT);
SetBkColor(hdcMem, crOld);
DeleteDC(hdcMem);
DeleteDC(hdcMem2);
return hbmMask;
}

// Draws bitmaps

void DrawBitmap(HDC hdc, HBITMAP hbmBitmap, HBITMAP hbmMask, int PosX, int PosY)
{
BITMAP bm;
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMask);
GetObject(hbmBitmap, sizeof(BITMAP), &bm);
BitBlt(hdc, PosX, PosY, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCAND);
SelectObject(hdcMem, hbmBitmap);
BitBlt(hdc, PosX, PosY, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCPAINT);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
}

// Draws text

void DrawText( HDC hdc, char* szText, char* szFont, int Size, COLORREF crColor, bool bBold, bool bItalic, bool bUnderline, int x, int y)
{
HFONT hfFont;
long lfHeight;
HDC hdc2 = GetDC(NULL);
lfHeight = -MulDiv(Size, GetDeviceCaps(hdc2, LOGPIXELSY), 72);
ReleaseDC(NULL, hdc2);
hfFont = CreateFont(lfHeight, 0, 0, 0, 700 * bBold, bItalic, bUnderline, 0, 0, 0, 0, 0, 0, szFont);
HFONT hfFontOld = (HFONT)SelectObject(hdc, hfFont);
SetBkColor(hdc, RGB(255,255,255));
SetTextColor(hdc, crColor);
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, x, y, szText, GetStringLength(szText));
SetTextColor(hdc, RGB(0,0,0));
SelectObject(hdc, hfFontOld);
DeleteObject(hfFont);
}

// Displays splash screen

void DisplaySplashScreen()
{
DrawBitmap(g_hdcBuffer, g_hbmSplashScreen, NULL, (g_rcClientRect.right - 800) / 2, (g_rcClientRect.bottom - 600) / 2);
UpdateBuffer();
Sleep(2000);
}

// Displays win screen

void DisplayWinScreen()
{
DrawBitmap(g_hdcBuffer, g_hbmWinScreen, NULL, (g_rcClientRect.right - 400) / 2, (g_rcClientRect.bottom - 300) / 2);
UpdateBuffer();
Sleep(2000);
}

// Displays lose screen

void DisplayLoseScreen()
{
DrawBitmap(g_hdcBuffer, g_hbmLoseScreen, NULL, (g_rcClientRect.right - 400) / 2, (g_rcClientRect.bottom - 300) / 2);
UpdateBuffer();
g_Score = 0;
g_BallsLeft = 5;
Sleep(3000);
}

// Initialization

int GameInit()
{
if(!QueryPerformanceFrequency((LARGE_INTEGER*)&ticknum)){
MessageBox(GameWindow, "High Performance Timers Not Supported", "Error", MB_OK | MB_ICONEXCLAMATION);
return 0;
}

ShowCursor(false);
g_Ball.x = 430;
g_Ball.y = 200;
g_Ball.speedx = 6;
g_Ball.speedy = -6;
g_Paddle.x = 400;
g_Paddle.y = 600;
g_Paddle.speedx = 8;
g_hbmBall = (HBITMAP)LoadImage(NULL,"PongBall.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
g_hbmBallMask = CreateBitmapMask(g_hbmBall, RGB(255,255,255));
g_hbmPaddle = (HBITMAP)LoadImage(NULL,"PongPaddle.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
g_hbmPaddleMask = CreateBitmapMask(g_hbmPaddle, RGB(255,255,255));
g_hbmSplashScreen = (HBITMAP)LoadImage(NULL,"UltraPongSplashScreen.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
g_hbmWinScreen = (HBITMAP)LoadImage(NULL,"UltraPongYouWin.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
g_hbmLoseScreen = (HBITMAP)LoadImage(NULL,"UltraPongYouLose.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);

g_hDC = GetDC(GameWindow);
GetClientRect(GameWindow, &g_rcClientRect);
g_hdcBuffer = CreateCompatibleDC(g_hDC);
g_hbmBuffer = CreateCompatibleBitmap(g_hDC, g_rcClientRect.right, g_rcClientRect.bottom);
g_hbmOldBuffer = (HBITMAP)SelectObject(g_hdcBuffer, g_hbmBuffer);
FillRect(g_hdcBuffer, &g_rcClientRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
UpdateBuffer();
DisplaySplashScreen();
while(!(GetAsyncKeyState(VK_SPACE) & 0x8000))
{
ClearBuffer();
DrawText(g_hdcBuffer, "Press Space Bar to Start", "Comic Sans MS", 20, RGB(0,0,255), 1, 0, 0, ((g_rcClientRect.right - 300) / 2), ((g_rcClientRect.bottom - 30) / 2));
UpdateBuffer();
}
return 0;
}

// Main game loop

int GameLoop()
{
QueryPerformanceCounter((LARGE_INTEGER*)&lastcount);
ClearBuffer();
if((GetAsyncKeyState(VK_LBUTTON) & 0x8000) || (GetAsyncKeyState(VK_ESCAPE) & 0x8000))
{
g_GameDone = true;
}
if(GetAsyncKeyState(VK_RIGHT) & 0x8000)
{
if((g_Paddle.x + 128) < g_rcClientRect.right)
{
g_Paddle.x += g_Paddle.speedx;
}
}
if(GetAsyncKeyState(VK_LEFT) & 0x8000)
{
if(g_Paddle.x > g_rcClientRect.left)
{
g_Paddle.x -= g_Paddle.speedx;
}
}
if(g_Ball.x < g_rcClientRect.left)
{
g_Ball.speedx *= -1;
}
if((g_Ball.x + 24) > g_rcClientRect.right)
{
g_Ball.speedx *= -1;
}
if(((g_Ball.y + 24) > g_Paddle.y) && ((g_Ball.y + 24) < (g_Paddle.y + 4)) && ((g_Ball.x + 24) > g_Paddle.x) && (g_Ball.x < (g_Paddle.x + 128)))
{
g_Ball.speedy *= -1;
g_Score += 10;
}
if(g_Ball.y < g_rcClientRect.top)
{
g_Ball.speedy *= -1;
}
if(g_Ball.y > g_rcClientRect.bottom)
{
g_BallsLeft -= 1;
if(g_BallsLeft == 0)
{
DisplayLoseScreen();
}
g_Ball.x = rand()%g_rcClientRect.right;
g_Ball.y = 200;
g_Ball.speedx = 6;
g_Ball.speedy = -6;
while(!(GetAsyncKeyState(VK_SPACE) & 0x8000) && !g_GameDone)
{
ClearBuffer();
if((GetAsyncKeyState(VK_LBUTTON) & 0x8000) || (GetAsyncKeyState(VK_ESCAPE) & 0x8000))
{
g_GameDone = true;
}
if(GetAsyncKeyState(VK_RIGHT) & 0x8000)
{
if((g_Paddle.x + 128) < g_rcClientRect.right)
{
g_Paddle.x += g_Paddle.speedx;
}
}
if(GetAsyncKeyState(VK_LEFT) & 0x8000)
{
if(g_Paddle.x > g_rcClientRect.left)
{
g_Paddle.x -= g_Paddle.speedx;
}
}
wsprintf(g_ScoreString,"%d",g_Score);
wsprintf(g_BallsLeftString,"%d",g_BallsLeft);
DrawText(g_hdcBuffer,g_ScoreString,"Comic Sans MS",30,RGB(0,255,0),1,0,0,10,(g_rcClientRect.bottom - 60));
DrawText(g_hdcBuffer,g_BallsLeftString,"Comic Sans MS",30,RGB(0,255,0),1,0,0,(g_rcClientRect.right - 120),(g_rcClientRect.bottom - 60));
DrawText(g_hdcBuffer, "Press Space Bar to Lanch", "Comic Sans MS", 20, RGB(0,0,255), 1, 0, 0, ((g_rcClientRect.right - 320) / 2), ((g_rcClientRect.bottom - 30) / 2));
DrawBitmap(g_hdcBuffer,g_hbmPaddle,g_hbmPaddleMask,g_Paddle.x,g_Paddle.y);
UpdateBuffer();

QueryPerformanceCounter((LARGE_INTEGER*)&newcount);
tickdiff = newcount - lastcount;
lastcount = newcount;
while(round(ticknum/fps) >= tickdiff)
{
QueryPerformanceCounter((LARGE_INTEGER*)&newcount);
tickdiff += newcount - lastcount;
lastcount = newcount;
}
}
}
g_Ball.x += g_Ball.speedx;
g_Ball.y += g_Ball.speedy;
wsprintf(g_ScoreString,"%d",g_Score);
wsprintf(g_BallsLeftString,"%d",g_BallsLeft);
DrawText(g_hdcBuffer,g_ScoreString,"Comic Sans MS",30,RGB(0,255,0),1,0,0,10,(g_rcClientRect.bottom - 60));
DrawText(g_hdcBuffer,g_BallsLeftString,"Comic Sans MS",30,RGB(0,255,0),1,0,0,(g_rcClientRect.right - 120),(g_rcClientRect.bottom - 60));
DrawBitmap(g_hdcBuffer,g_hbmPaddle,g_hbmPaddleMask,g_Paddle.x,g_Paddle.y);
DrawBitmap(g_hdcBuffer,g_hbmBall,g_hbmBallMask,g_Ball.x,g_Ball.y);
UpdateBuffer();

QueryPerformanceCounter((LARGE_INTEGER*)&newcount);
tickdiff = newcount - lastcount;
lastcount = newcount;
while(round(ticknum/fps) >= tickdiff)
{
QueryPerformanceCounter((LARGE_INTEGER*)&newcount);
tickdiff += newcount - lastcount;
lastcount = newcount;
}
return 0;
}

// Shutdown

int GameShutdown()
{
SelectObject(g_hdcBuffer, g_hbmOldBuffer);
DeleteDC(g_hdcBuffer);
DeleteObject(g_hbmBuffer);
ReleaseDC(GameWindow, g_hDC);
return 0;
}

// Message handler

LRESULT CALLBACK GameProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ){

switch(msg)
{
case WM_CLOSE:
g_GameDone = true;
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}

// WinMain function for the game

int APIENTRY WinMain( HINSTANCE p_instance, HINSTANCE p_prev_instance, LPSTR p_cmd_line, int p_show )
{
MSG msg;

GameInstance = p_instance;

WNDCLASS GameWindowClass;

GameWindowClass.style = CS_OWNDC;
GameWindowClass.cbClsExtra = 0;
GameWindowClass.cbWndExtra = 0;
GameWindowClass.hInstance = GameInstance;
GameWindowClass.hIcon = LoadIcon(NULL,IDI_WINLOGO);
GameWindowClass.hCursor = LoadCursor(NULL,IDC_ARROW);
GameWindowClass.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
GameWindowClass.lpszMenuName = NULL;
GameWindowClass.lpszClassName = "Game Window Class";
GameWindowClass.lpfnWndProc = GameProc;

if(!RegisterClass(&GameWindowClass)){
MessageBox(GameWindow, "Couldn't register class", "Error", MB_OK | MB_ICONEXCLAMATION);
return 0;
}

GameWindow = CreateWindow("Game Window Class",GameTitle,WS_POPUP | WS_MAXIMIZE | WS_VISIBLE,0,0,640,480,NULL,NULL,GameInstance,NULL);

GameInit();
while(!g_GameDone){
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GameLoop();
}

if(!DestroyWindow(GameWindow)){
MessageBox(GameWindow, "Couldn't destroy window", "Error", MB_OK | MB_ICONEXCLAMATION);
return 0;
}

GameWindow = NULL;

if(!UnregisterClass("Game Window Class",GameInstance)){
MessageBox(GameWindow, "Couldn't unregister class", "Error", MB_OK | MB_ICONEXCLAMATION);
return 0;
}

return 0;
}


sorry, im on dial up, putting the bitmaps up would take forever, but heres the source if anyone's interested.

[edited by - JIMbond21 on September 5, 2003 7:16:00 PM]

Share this post


Link to post
Share on other sites
Here is your Pong in a neat format...

PONG.txt

I would like to help solve your tearing problem in GDI since I'll be finishing a PacMan clone in GDI with Bitmaps also....

I wonder why you don't call your ClearBuffer and UpdateBuffer one time here:

while(!g_GameDone)
{

if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

ClearBuffer();
GameLoop();
UpdateBuffer();

}

This seems much easier to understand. I see it called a couple times. Why not clear it once, let GameLoop do all your drawing, then UpdateBuffer once, then wait to synch to 30 frames/second.

Your Sleep(2000) and Sleep(3000) may be causing the tearing? Not sure why that's necessary... Another thing I notice is you do DeleteDC each time in your DrawBitMap function. If I understand correctly, the DC for the Bitmaps can be left allocated throughout the game. The CreateCompatibleDC can be done in your GameInit (you need 3 mem DC allocated, two for paddles, one for ball), then three DeleteDC at your GameShutDown. I'm learning Win Bitmaps as well, and trying to figure out the best way to do this....with no tearing, flickering, flearing, shearing, or tickering

Phil P

[edited by - PhilVaz on September 5, 2003 8:51:16 PM]

Share this post


Link to post
Share on other sites
I havent'' done much with bitmaps in GDI, but I will say that calling DestroyDC every time a bitmap is drawn can''t be good, right? On a small sub-note, I rememeber when I first programmed Pong, I created a brush EVERY TIME I drew something. As you can imagine, I ran out of memory VERY quickly

Sleep(2000) is quite long for a real-time game... a two second delay?

Share this post


Link to post
Share on other sites
Yeah, fixing his tearing problem in a Pong game by suggesting "vertical retrace synch" is like trying to kill a flea with a steamhammer (from a old phrase I heard). It can't be that complex a problem. Let's make sure he (and I) got the basics down, like allocating the Bitmap memory and BitBlt correctly. If he can't solve tearing in a simple Pong game, there's no way my PacMan will look smooth either.

VazGames.com

Phil P

[edited by - PhilVaz on September 5, 2003 9:04:55 PM]

Share this post


Link to post
Share on other sites
<< I will fix the things suggested and let you know how it goes. >>

Let me know what happens. Also I see why you are doing Sleep(2000) and Sleep(3000) to wait for your DisplaySplashScreen, DisplayWinScreen, DisplayLoseScreen. I think a better way might be to use an int countdown, and display your text once a frame, and decrement countdown until it reaches zero. That way Windows doesn't get hogged of messages during the Sleep.

You should call your DisplaySplash, Win, and Lose Screen functions inside your GameLoop and use another variable, like int game_state, to switch between states.

When game_state = 0 you set countdown (to 5000 or whatever) and DisplaySplashScreen (and here you decrement countdown by 1 and when it reaches 0 you set game_state to 3)

When game_state = 1 you set countdown and DisplayWinScreen

When game_state = 2 you set countdown and DisplayLoseScreen

When 3 you are playing the game, etc. During playing of game and you win, set game_state = 1, when lose set game_state = 2

At the beginning of your GameLoop you need to check game_state

That's how Lamothe does it anyway (called a "finite state machine"). I assume most "pros" use this game_state type thing. It makes your logic clearer. All those functions get called from your GameLoop, you just have to test for game_state first.

Phil P

[edited by - PhilVaz on September 5, 2003 11:04:28 PM]

Share this post


Link to post
Share on other sites
Phil, I tried allocating the memeory for the DC's for the blitting of the bitmaps in the GameInit function and deleting them in the GameShutdown, so now I have draw functions for each the paddle and the ball with only the blitting because the DC's are now global, I thought as well that this would help a lot. The tearing acctually got a little worse when making the bitmap DC's global and allocated thoughtout the game, I'm stumpped again as to why I can't fix this tearing problem. For now I have stopped trying to finish the game and I'm trying to fix the graphics issues, if you have any other thoughts let me know.
Oh, one more thing, if you use double buffering for your game, how do you go about doing it, because I use an hdc in memory. I'm not sure if there is another way to do it.

[edited by - JIMbond21 on September 6, 2003 7:38:54 PM]

Share this post


Link to post
Share on other sites
<< Oh, one more thing, if you use double buffering for your game, how do you go about doing it, because I use an hdc in memory. I'm not sure if there is another way to do it. >>

I got my double buffer code from GameTutorials.com, not sure what else to tell you to fix the tearing. Hopefully I'll be finishing a pacman clone in a few days with bitmaps, with I hope no tearing. Here is my asteroids game that uses double buffer, very smooth but I use only GDI primitives (no bitmaps)

Vazteroids.cpp EXE

Also my simple cat animation bitmap demo, do you see tearing here? (I slowed it down with a countdown, it could be much faster) I don't see any tearing but it is quite small....

CatDemo.cpp EXE

Phil P

[edited by - PhilVaz on September 7, 2003 8:08:45 AM]

Share this post


Link to post
Share on other sites