2011-01-14 37 views
2

你會如何定期在窗戶上繪製東西。如何使用Win32 API爲窗口設置動畫?

我已經拿出這個(剝離了不少爲清楚起見)

#include <windows.h> 

void DrawOntoDC (HDC dc) { 

    pen = CreatePen(...) 
    penOld = SelectObject(dc, pen) 

    ..... Here is the actual drawing, that 
    ..... should be regurarly called, since 
    ..... the drawn picture changes as time 
    ..... progresses 

    SelectObject(dc, pen_old); 

    DeleteObject(pen); 
} 



LRESULT CALLBACK WindowProc(....) { 
    switch(Msg) { 

    case WM_PAINT: { 
     PAINTSTRUCT ps; 
      dc = BeginPaint(hWnd, &ps); 

     ..... A Memory DC is created 
     ..... In order to prevent flickering. 

     HBITMAP PersistenceBitmap; 
     PersistenceBitmap = CreateCompatibleBitmap(dc, windowHeight, windowHeight); 

     HDC  dcMemory = CreateCompatibleDC(dc); 
     HBITMAP oldBmp = (HBITMAP) SelectObject(dcMemory, PersistenceBitmap); 

     DrawOntoDC(dcMemory); 

     ..... "copying" the memory dc in one go unto dhe window dc: 

     BitBlt (dc, 
        0, 0, windowWidth, windowHeight, 
        dcMemory, 
        0, 0, 
        SRCCOPY 
       ); 

     ..... destroy the allocated bitmap and memory DC 
     ..... I have the feeling that this could be implemented 
     ..... better, i.e. without allocating and destroying the memroy dc 
     ..... and bitmap with each WM_PAINT. 

     SelectObject(dcMemory, oldBmp); 
     DeleteDC(dcMemory); 
     DeleteObject(PersistenceBitmap); 

    EndPaint (hWnd, &ps); 
     return 0; 
    } 
    default: 
     return DefWindowProc(hWnd, Msg, wParam, lParam); 
    } 
} 


DWORD WINAPI Timer(LPVOID p) { 

    ..... The 'thread' that makes sure that the window 
    ..... is regularly painted. 

    HWND hWnd = (HWND) *((HWND*) p); 

    while (1) { 
    Sleep(1000/framesPerSecond); 
    InvalidateRect(hWnd, 0, TRUE); 
    } 
} 


int APIENTRY WinMain(...) { 

    WNDCLASSEX windowClass; 
     windowClass.lpfnWndProc   = WindowProc; 
     windowClass.lpszClassName  = className; 
     .... 

    RegisterClassEx(&windowClass); 

    HWND hwnd = CreateWindowEx(
       .... 
       className, 
       ....); 


    ShowWindow(hwnd, SW_SHOW); 
    UpdateWindow(hwnd); 

    DWORD threadId; 
    HANDLE hTimer = CreateThread(
     0, 0, 
     Timer, 
    (LPVOID) &hwnd, 
    0, &threadId); 

    while(GetMessage(&Msg, NULL, 0, 0)) { 
     .... 
    } 

    return Msg.wParam; 
} 

我想有很多可以改進的,我會很感激任何指針的東西我都忽略了。

回答

4

用工作線程做這種事情並不是最優的。 由於對繪畫的最佳代碼路徑總是通過留下兩種方法可以做到這一個WM_PAINT:

  1. GUI線程上只需創建一個定時器,直接到的TimerProc,或窗口張貼WM_TIMER消息,並調用引擎的OnTick()部分。如果任何精靈移動,他們使用InvalidateRect()使其區域失效,並通過自動發佈WM_PAINT來跟蹤窗口。如果遊戲相對閒置,這具有CPU使用率非常低的優點。

  2. 大多數遊戲都希望使用低優先級的基於WM_TIMER的計時器來實現更嚴格的計時。在這種情況下,要實現一個遊戲循環是這樣的:

消息循環:

while(stillRunning) 
{ 
    DWORD ret = MsgWaitForMultipleObjects(0,NULL,FALSE,frameIntervalMs,QS_ALLEVENTS); 
    if(ret == WAIT_OBJECT_0){ 
    while(PeekMessage(&msg,0,0,0,PM_REMOVE)){ 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    if(TickGame()) // if TickGame indicates that enough time passed for stuff to change 
    RedrawWindow(hwndGame,...); // Dispatch a WM_PAINT immediately. 
} 

用這種消息循環的危險是,如果...申請進入任何一種模態狀態: - 用戶開始拖動窗口/彈出一個模態對話框,然後消息被模態循環泵送,所以動畫停止。因此,如果需要將高性能消息循環與模態操作混合,則需要有一個回退計時器。


WRT你的WM_PAINT實現 - 它通常更好地(重新)創建你的backbuffer來響應WM_SIZE消息。這樣它總是合適的大小,並且不會產生每秒多次重新創建大容量內存緩衝區的相當大的成本。