2011-09-13 47 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); 
} 

試圖通過這個實施意見應該可以幫助您瞭解您的方式工作。希望這可以幫助。