2011-09-13 53 views
0

我一直在使用win32 api制作一个带有精灵的游戏。出于某种原因,当我在屏幕上显示多个精灵时,偶尔会闪烁,好像它们正在消失并返回。当屏幕上只有一个精灵时,它会正确显示。当屏幕上有多个精灵时,win32位图闪烁

我使用C++,WIN32 API和与Visual Studio 08

以下工作大致是什么,我有:

//creates rect based on window client area 
GetClientRect(ghwnd, &screenRect); 
// Initialises front buffer device context (window) 
frontHDC = GetDC(ghwnd);  
// sets up Back DC to be compatible with the front 
backHDC = CreateCompatibleDC(frontHDC); 
// Create another hdc to store the bitmap in before the backbuffer 
bitmapHDC = CreateCompatibleDC(frontHDC); 
//creates bitmap compatible with the front buffer 
theOldFrontBitMap = CreateCompatibleBitmap(frontHDC, screenRect.right, screenRect.bottom); 
//creates bitmap compatible with the back buffer 
theOldBackBitMap = (HBITMAP)SelectObject(backHDC, theOldFrontBitMap); 

HBITMAP originalBitMap = (HBITMAP)SelectObject(bitmapHDC,bitmap); 

//Transparency function 
TransparentBlt(backHDC, 
       m_Position.x, 
       m_Position.y, 
       m_Size.x, 
       m_Size.y, 
       bitmapHDC, 
       0, 
       0, 
       m_Size.x, 
       m_Size.y, 
       0x00FFFFFF); 

SelectObject(bitmapHDC,originalBitMap); 

BitBlt(frontHDC, screenRect.left, screenRect.top, 
     screenRect.right, screenRect.bottom, backHDC, 0, 0, SRCCOPY); 

我是不是做正确吗?如果是的话我哪里错了?如果我没有提供足够的信息,请告诉我,我会纠正这一点。

回答

1

创建Win32游戏的问题是,即使使用双缓冲,也无法等待显示器显示缓冲区的垂直回扫。

垂直回扫正在进行时显示缓冲区或精灵可能会导致撕裂或甚至消失的精灵,你遇到。

解决此问题的唯一方法是使用像OpenGL或DirectX这样的SDK来管理和显示缓冲区。

下面是一个示例程序,它可以帮助你,请使用箭头键移动白盒上的双缓冲背景:

#include <Windows.h> 

RECT rcSize; 
HDC hdcBackBuffer, hdcSprite; 
HBITMAP hbmBackBuffer, hbmSprite; 
int spriteX = 175, spriteY = 175; 

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
    static PAINTSTRUCT ps; 

    switch (msg) 
    { 
    case WM_CREATE: 
     { 
      HDC hdcWindow = GetDC(hWnd); 

      // make back buffer 
      GetClientRect(hWnd, &rcSize); 
      hdcBackBuffer = CreateCompatibleDC(hdcWindow); 
      hbmBackBuffer = CreateCompatibleBitmap(hdcBackBuffer, rcSize.right - rcSize.left, rcSize.bottom - rcSize.top); 
      SelectObject(hdcBackBuffer, hbmBackBuffer); // SHOULD SAVE PREVIOUS... 

      // make sprite 
      hdcSprite = CreateCompatibleDC(hdcWindow); 
      hbmSprite = CreateCompatibleBitmap(hdcSprite, 50, 50); 
      SelectObject(hdcSprite, hbmSprite); // SHOULD SAVE PREVIOUS... 
      RECT rcSprite; 
      SetRect(&rcSprite, 0, 0, 50, 50); 
      FillRect(hdcSprite, &rcSprite, (HBRUSH)GetStockObject(WHITE_BRUSH)); 

      ReleaseDC(hWnd, hdcWindow); 
      return 0; 
     } 
    case WM_KEYDOWN: 
     { 
      // SHOULD REALLY USE GetAsyncKeyState for game, but simplified here 
      switch (wParam) 
      { 
      case VK_LEFT: 
       spriteX--; 
       break; 
      case VK_RIGHT: 
       spriteX++; 
       break; 
      case VK_UP: 
       spriteY--; 
       break; 
      case VK_DOWN: 
       spriteY++; 
       break; 
      } 
      return 0; 
     } 
    case WM_ERASEBKGND: 
     { 
      return 1; // INDICATE THAT WE ERASED THE BACKGROUND OURSELVES 
     } 
    case WM_PAINT: 
     { 
      BeginPaint(hWnd, &ps); 
      // clear back buffer 
      FillRect(hdcBackBuffer, &rcSize, (HBRUSH)GetStockObject(BLACK_BRUSH)); 
      // render sprite to back buffer 
      BitBlt(hdcBackBuffer, spriteX, spriteY, 50, 50, hdcSprite, 0, 0, SRCCOPY); 
      // render back buffer to screen 
      BitBlt(ps.hdc, 0, 0, rcSize.right - rcSize.left, rcSize.bottom - rcSize.top, hdcBackBuffer, 0, 0, SRCCOPY); 
      EndPaint(hWnd, &ps); 
      return 0; 
     } 
    case WM_DESTROY: 
     { 
      // TODO - DESTROY ALL BITMAPS AND DEVICE CONTEXTS 
      PostQuitMessage(0); 
      return 0; 
     } 
    default: 
     { 
      return DefWindowProc(hWnd, msg, wParam, lParam); 
     } 
    } 
} 

int WINAPI WinMain(HINSTANCE hPrevInstance, HINSTANCE hInstance, LPSTR lpCmdLine, int nShowCmd) 
{ 
    static TCHAR className[] = TEXT("GameClass"); 
    static TCHAR windowName[] = TEXT("A Game"); 

    WNDCLASSEX wcex; 

    wcex.cbClsExtra = 0; 
    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.cbWndExtra = 0; 
    wcex.hbrBackground = NULL; 
    wcex.hCursor = LoadCursor(hInstance, IDC_ARROW); 
    wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION); 
    wcex.hIconSm = NULL; 
    wcex.hInstance = hInstance; 
    wcex.lpfnWndProc = WndProc; 
    wcex.lpszClassName = className; 
    wcex.lpszMenuName = NULL; 
    wcex.style = 0; 

    if (!RegisterClassEx(&wcex)) 
     return 0; 

    HWND hWnd = CreateWindow(className, windowName, WS_CAPTION | WS_BORDER | WS_SYSMENU, 0, 0, 400, 400, NULL, NULL, hInstance, NULL); 
    if (!hWnd) 
     return 0; 

    ShowWindow(hWnd, nShowCmd); 
    UpdateWindow(hWnd); 

    MSG msg; 
    for (;;) 
    { 
     if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
     { 
      if (msg.message == WM_QUIT) 
      { 
       break; 
      } 
      else 
      { 
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      } 
     } 

     InvalidateRect(hWnd, NULL, FALSE); 
    } 

    return msg.wParam; 
} 
+0

我不认为问题与垂直回溯有关,但我明白你在说什么。在我寻找解决方案时,我看到了无数的win32游戏教程,当我运行它们时,它们都显得很好。我以前从来没有遇到任何问题,但之前我只使用过两次win32和sprites。我确信我没有以某种形式正确地进行缓冲。 – Dave

+0

这会导致称为*撕裂*的不同问题,在移动的对象上可见。这听起来像是一个双缓冲问题,但不可能告诉这个代码的上下文可能是什么。 Anyhoo,不要在WM_ERASEBKGND消息处理程序中做任何事情。 –

+0

你不需要前面的位图。你需要两个位图。后台缓冲区位图和Sprite位图。在WM_PAINT中,清除后台缓冲区并将精灵绘制到它上面。然后,您将后台缓冲区显示在窗口上(即GetDC()) – Helper

1

我觉得你的背部缓冲区实现是错误的,虽然林不知道究竟在何处。尝试一个单独的后台缓冲区类的实现。我希望它有帮助。

这里是我的后台缓冲区类。

#ifndef BACKBUFFER_H 
#define BACKBUFFER_H 

#include <Windows.h> 


class BackBuffer 
{ 
public: 
    BackBuffer(HWND hWnd, int width, int height); 
    ~BackBuffer(); 

    HDC getDC(); 

    int width(); 
    int height(); 

    void present(); 

private: 
    // Make copy constructor and assignment operator private 
    // so client cannot copy BackBuffers. We do this because 
    // this class is not designed to be copied because it 
    // is not efficient--copying bitmaps is slow (lots of memory). 
    // In addition, most applications will probably only need one 
    // BackBuffer anyway. 
    BackBuffer(const BackBuffer& rhs); 
    BackBuffer& operator=(const BackBuffer& rhs); 
private: 
    HWND mhWnd; 
    HDC  mhDC; 
    HBITMAP mhSurface; 
    HBITMAP mhOldObject; 
    int  mWidth; 
    int  mHeight; 
}; 


#endif //BACKBUFFER_H 

继承人的实现:

BackBuffer::BackBuffer(HWND hWnd, int width, int height) 
{ 
    //Save a copy of the main window handle 
    mhWnd = hWnd; 

    //Get a handle to the device context associated with 
    // the window 
    HDC hWndDC = GetDC(hWnd); 

    //Save the backbuffer dimensions 
    mWidth = width; 
    mHeight = height; 

    //Create system memory device context that is compatible 
    //with the window one 
    mhDC = CreateCompatibleDC(hWndDC); 

    //Create the backbuffer surface bitmap that is compatible 
    //with the window device context bitmap format. That is 
    //the surface we will render onto. 
    mhSurface = CreateCompatibleBitmap(hWndDC, width, height); 

    //Done with DC 
    ReleaseDC(hWnd, hWndDC); 

    //At this point, the back buffer surface is uninitialized, 
    //so lets clear it to some non-zero value. Note that it 
    //needs to be a non-zero. If it is zero then it will mess 
    //up our sprite blending logic. 

    //Select the backbuffer bitmap into the DC 
    mhOldObject = (HBITMAP)SelectObject(mhDC, mhSurface); 

    //Select a white brush 
    HBRUSH white = (HBRUSH)GetStockObject(WHITE_BRUSH); 
    HBRUSH oldBrush = (HBRUSH)SelectObject(mhDC, white); 

    //Clear the backbuffer rectangle 
    Rectangle(mhDC, 0, 0, mWidth, mHeight); 

    //Restore the original brush 
    SelectObject(mhDC, oldBrush); 

} 

BackBuffer::~BackBuffer() 
{ 
    SelectObject(mhDC, mhOldObject); 
    DeleteObject(mhSurface); 
    DeleteDC(mhDC); 
} 

HDC BackBuffer::getDC() 
{ 
    return mhDC; 
} 

int BackBuffer::width() 
{ 
    return mWidth; 
} 

int BackBuffer::height() 
{ 
    return mHeight; 
} 

void BackBuffer::present() 
{ 
    //Get a handle to the device context associated with 
    //the window 
    HDC hWndDC = GetDC(mhWnd); 

    //Copy the backbuffer contents over to the 
    //window client area 
    BitBlt(hWndDC, 0, 0, mWidth, mHeight, mhDC, 0, 0, SRCCOPY); 

    //Free window DC when done 
    ReleaseDC(mhWnd, hWndDC); 
} 

试图通过这个实施意见应该可以帮助您了解您的方式工作。希望这可以帮助。