Pregunta

He estado usando la API Win32 para hacer un juego con Sprites. Por alguna razón, cuando tengo más de un sprite en la pantalla, parpadean ocasionalmente como si estuvieran desapareciendo y regresando. Cuando solo hay un sprite en la pantalla, se muestra correctamente.

Estoy usando C ++, Win32 API y trabajando con Visual Studio 08

Lo siguiente es más o menos lo que tengo:

//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);

¿Estoy haciendo esto correctamente? Y si es así, ¿dónde me estoy equivocando? Si no he dado suficiente información, dígame y lo rectificaré.

¿Fue útil?

Solución

El problema con la creación de un juego Win32 es que, incluso si usa doble almacenamiento en búfer, no tiene forma de esperar a que el retroceso vertical del monitor muestre el búfer.

Mostrar el búfer o el sprite mientras el retroceso vertical está en progreso puede causar desgarro o incluso el sprite que desaparece que experimenta.

La única forma real en esto es usar un SDK como OpenGL o DirectX para administrar y mostrar los buffers.

Aquí hay un programa de muestra que puede ayudarlo, use las teclas de flecha para mover la caja blanca en el fondo de doble almacenamiento en búfer:

#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;
}

Otros consejos

Creo que la implementación de su búfer de espalda es incorrecta, aunque no estoy seguro exactamente dónde. Pruebe esta implementación de una clase de búfer de espalda separada. Espero que ayude.

Aquí mi clase de búfer de espalda.

#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

Aquí está la implementación:

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);
}

Trate de abrirse camino a través de esta implementación, los comentarios deberían ayudarlo a comprender. Espero que esto ayude.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top