Domanda

Ho usato l'API Win32 per fare una partita con Sprites. Per qualche motivo quando ho più di uno sprite sullo schermo che lampeggiano di tanto in tanto come se stessero scomparendo e ritornando. Quando c'è solo uno sprite sullo schermo viene visualizzato correttamente.

Sto usando C ++, API Win32 e lavoro con Visual Studio 08

Quello che segue è all'incirca quello che ho:

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

Lo sto facendo correttamente? E se sì, dove vado male? Se non ho fornito informazioni sufficienti per favore dimmelo e lo rettificherò.

È stato utile?

Soluzione

Il problema con la creazione di un gioco Win32 è che, anche se usi il doppio buffering, non hai modo di attendere la retrace verticale del monitor per visualizzare il buffer.

Visualizzazione del buffer o dello sprite mentre è in corso la ritratta verticale può causare lacrima o persino lo sprite scomparente che si verifica.

L'unico vero modo per aggirare questo è utilizzare un SDK come OpenGL o DirectX per gestire e visualizzare i buffer.

Ecco un programma di esempio che può aiutarti, usa i tasti freccia per spostare la scatola bianca sullo sfondo tamponato a doppia:

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

Altri suggerimenti

Penso che la tua implementazione del buffer posteriore sia sbagliata anche se non sono sicuro di dove. Prova questa implementazione di una classe buffer back separata. Spero possa essere d'aiuto.

Qui la mia classe di buffer posteriore.

#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

Ecco l'implementazione:

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

Prova a farti strada attraverso questa implementazione i commenti dovrebbero aiutarti a capire. Spero che sia di aiuto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top