Растровые изображения Win32 мигают, когда на экране более одного спрайта - PullRequest
0 голосов
/ 13 сентября 2011

Я использую Win32 API для создания игры со спрайтами. По какой-то причине, когда у меня есть несколько спрайтов на экране, они время от времени мигают, как будто они исчезают и возвращаются. Когда на экране присутствует только один спрайт, он отображается правильно.

Я использую 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);

Я правильно это делаю? и если да, то где я иду не так? Если я не предоставил достаточно информации, пожалуйста, сообщите мне, и я исправлю это.

Ответы [ 2 ]

2 голосов
/ 13 сентября 2011

Проблема с созданием игры для Win32 заключается в том, что даже если вы используете двойную буферизацию, у вас нет возможности ждать вертикального отклика монитора для отображения буфера.

Отображение буфера или спрайта во времявертикальное восстановление может вызвать разрыв или даже исчезающий спрайт, который вы испытываете.

Единственный реальный способ обойти это - использовать SDK, например OpenGL или DirectX, для управления и отображения буферов.

Вот пример программы, которая может вам помочь, используйте клавиши со стрелками для перемещения белого поля на фоне с двойной буферизацией:

#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;
}
1 голос
/ 10 мая 2016

Я думаю, что ваша реализация обратного буфера неверна, хотя я не уверен, где именно. Попробуйте эту реализацию отдельного класса обратного буфера. Надеюсь, это поможет.

Вот мой задний буферный класс.

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

Постарайтесь проработать эту реализацию, комментарии должны помочь вам понять. Надеюсь это поможет.

...