Win32 游戏循环

Win32 game loop

我正在使用 Win32 API 创建简单的游戏。当我点击 window 时,出现一个球并开始滚动,看起来像 bida 游戏

我的问题是当我调用 "InvalidateRect" 时,我的游戏非常滞后。我不知道我做错了什么!!!

而且我的 hPen 没有像我期望的那样工作

求助!!! 谢谢你,对不起我的英语不好

.386    ; use 80386 instruction
.model flat,stdcall ; uses flat memory addressing model
option casemap:none

include C:\masm32\include\windows.inc   ; windows.inc have structures and constants
include C:\masm32\include\user32.inc 
includelib C:\masm32\lib\user32.lib ; CreateWindowEx, RegisterClassEx,...
include C:\masm32\include\kernel32.inc 
includelib C:\masm32\lib\kernel32.lib   ; ExitProcess
include C:\masm32\include\masm32.inc
includelib C:\masm32\lib\masm32.lib
include C:\masm32\include\gdi32.inc 
includelib C:\masm32\lib\gdi32.lib 

.CONST
DRAWING equ 1
WAITING equ 0
PEN_COLOR equ 00000000h ; black
PEN_SIZE equ 2
BALL_SIZE equ 35
BALL_SPEED equ 20

.DATA
ClassName db 'SimpleWinClass',0
AppName db 'Ball',0

state db WAITING

vectorX dd 6
vectorY dd -7

WIN_WIDTH dd 700
WIN_HEIGHT dd 500

.DATA?
; HINSTANCE & LPSTR typedef DWORD in windows.inc
; reserve the space for future use
hInstance HINSTANCE ?

tlpoint POINT <>
brpoint POINT <>

; use for create window
wc WNDCLASSEX <?>
msg MSG <?> ; handle message
hwnd HWND ? ; handle window procedure

hdc HDC ?
ps PAINTSTRUCT <?>

time SYSTEMTIME <?>

hPen HPEN ?

.CODE
start:
    ; call GetModuleHandle(null)
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx
    push NULL
    call GetModuleHandle    ; module handle same as instance handle in Win32
    mov hInstance, eax  ; return an instance to handle in eax

    ; call WinMain(hInstance, hPrevInstance, CmdLine, CmdShow)
    ; our main function
    push SW_SHOW
    push NULL
    push NULL
    push hInstance
    call WinMain

    ; call ExitProcess
    push eax
    call ExitProcess

    ; Define WinMain 
    WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
        ; Structure in msdn, define in windows.inc
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx

        ; Load default icon
        push IDI_APPLICATION
        push NULL
        call LoadIcon
        mov wc.hIcon, eax
        mov wc.hIconSm, eax

        ; Load default cursor
        push IDC_ARROW
        push NULL
        call LoadCursor
        mov wc.hCursor, eax

        mov wc.cbSize, SIZEOF WNDCLASSEX    ; size of this structure
        mov wc.style, CS_HREDRAW or CS_VREDRAW  ; style of windows https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
        mov wc.lpfnWndProc, OFFSET WndProc  ; andress of window procedure
        mov wc.cbClsExtra, NULL 
        mov wc.cbWndExtra, NULL
        push hInstance
        pop wc.hInstance
        mov wc.hbrBackground, COLOR_WINDOW+1    ; background color, require to add 1
        mov wc.lpszMenuName, NULL
        mov wc.lpszClassName, OFFSET ClassName

        ; we register our own class, named in ClassName
        push offset wc
        call RegisterClassEx

        ; after register ClassName, we use it to create windows compond
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
        push NULL
        push hInstance
        push NULL
        push NULL
        push WIN_HEIGHT
        push WIN_WIDTH
        push CW_USEDEFAULT
        push CW_USEDEFAULT
        push WS_OVERLAPPEDWINDOW
        push offset AppName
        push offset ClassName
        push WS_EX_CLIENTEDGE
        call CreateWindowEx

        mov hwnd, eax   ; return windows handle

        ; display window
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
        push CmdShow
        push hwnd
        call ShowWindow

        ; update window
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/dd145167(v=vs.85).aspx
        push hwnd
        call UpdateWindow

        ; Message Loop
        MESSAGE_LOOP:
            ; get message
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
            push PM_REMOVE
            push 0
            push 0
            push NULL
            push offset msg
            call PeekMessage

            ; return in eax
            ; if the function retrieves a message other than WM_QUIT, the return value is nonzero.
            ; if the function retrieves the WM_QUIT message, the return value is zero.
            cmp eax, 0
            je GAME_LOOP

            cmp msg.message, WM_QUIT
            je END_LOOP

            ; translate virtual-key messages into character messages - ASCII in WM_CHAR
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955(v=vs.85).aspx
            push offset msg
            call TranslateMessage 

            ; sends the message data to the window procedure responsible for the specific window the message is for.
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644934(v=vs.85).aspx
            push offset msg
            call DispatchMessage

        GAME_LOOP:
            ; check that is DRAWING or not?
            cmp [state], DRAWING
            jne MESSAGE_LOOP

            push offset time
            call GetSystemTime

            cmp dword ptr[time.wMilliseconds], BALL_SPEED
            jl MESSAGE_LOOP

            push TRUE
            push NULL
            push hwnd
            call InvalidateRect

            jmp MESSAGE_LOOP

        END_LOOP:
            mov eax, msg.wParam   
        ret 
    WinMain endp

    ; Handle message with switch(notification)
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
    WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
        cmp uMsg, WM_PAINT
        je ON_WM_PAINT

        cmp uMsg, WM_CREATE
        je ON_WM_CREATE

        cmp uMsg, WM_LBUTTONDOWN
        je ON_WM_LBUTTONDOWN

        cmp uMsg, WM_DESTROY
        je ON_WM_DESTROY

        cmp uMsg, WM_QUIT
        je ON_WM_DESTROY

        cmp uMsg, WM_CLOSE
        je ON_WM_DESTROY

        jmp ON_DEFAULT

        ; user close program
        ON_WM_DESTROY:
            push NULL
            call PostQuitMessage
            jmp EXIT

        ON_WM_CREATE:
            ; create a pen with specific color and size
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/dd183509(v=vs.85).aspx
            push PEN_COLOR
            push PEN_SIZE
            push PS_SOLID
            call CreatePen
            mov hPen, eax

            jmp EXIT

        ON_WM_LBUTTONDOWN:
            cmp [state], DRAWING
            je EXIT

            push lParam
            call updateXY

            ; when clicked, set state to DRAWING
            mov [state], DRAWING

            mov dword ptr[time.wMilliseconds], BALL_SPEED
            push offset time
            call SetSystemTime

            jmp EXIT

        ON_WM_PAINT:
            mov dword ptr[time.wMilliseconds], 0
            push offset time
            call SetSystemTime 

            push offset ps
            push hWnd 
            call BeginPaint
            mov hdc, eax

            ; apply pen to hdc
            push hPen
            push hdc
            call SelectObject

            call createEllipse

            push offset ps
            push hWnd
            call EndPaint

            jmp EXIT

        ON_DEFAULT:
            ; handle any message that program don't handle
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
            push lParam 
            push wParam 
            push uMsg   ; message
            push hWnd   ; windows
            call DefWindowProc

            jmp EXIT

        EXIT:
            ret
    WndProc endp

    createEllipse proc
        push brpoint.y
        push brpoint.x
        push tlpoint.y
        push tlpoint.x
        push hdc
        call Ellipse

        call moveEllipse

        mov eax, WIN_WIDTH
        cmp brpoint.x, eax
        jg MEET_RIGHT_LEFT

        mov eax, WIN_HEIGHT
        cmp brpoint.y, eax
        jg MEET_BOTTOM_TOP

        cmp tlpoint.x, 0
        jl MEET_RIGHT_LEFT

        cmp tlpoint.y, 0
        jl MEET_BOTTOM_TOP

        jmp MEET_NONE

        MEET_RIGHT_LEFT:
            neg vectorX
            jmp MEET_NONE

        MEET_BOTTOM_TOP:
            neg vectorY
            jmp MEET_NONE

        MEET_NONE:

        ret
    createEllipse endp

    moveEllipse proc
        mov eax, dword ptr[vectorX]
        mov ecx, dword ptr[vectorY]

        add tlpoint.x, eax
        add tlpoint.y, ecx
        add brpoint.x, eax
        add brpoint.y, ecx

        ret
    moveEllipse endp

    updateXY proc lParam:LPARAM
        mov eax, lParam

        ; get low word that contain x
        xor ebx, ebx
        mov bx, ax

        mov tlpoint.x, ebx
        mov brpoint.x, ebx
        add brpoint.x, BALL_SIZE

        ; get high word that contain y
        mov eax, lParam
        shr eax, 16

        mov tlpoint.y, eax
        mov brpoint.y, eax
        add brpoint.y, BALL_SIZE

        ret
    updateXY endp

end start

您正在 运行 您的 GAME_LOOP 之间处理一条消息。这可不是什么好兆头。相反,在屏幕更新之间清空整个消息队列。解决方法很简单:Add

jmp MESSAGE_LOOP

紧接着

call DispatchMessage

使用系统时间来解决速度问题不是一个好主意。请改用您的程序 moveEllipse 并增加 vectorXvectorY 以获得更快的速度。如果需要真正的更新,应该调用 InvalidateRec。您可以通过使用计时器而不是更改和检查系统时间来实现:SetTimer.

.386    ; use 80386 instruction
.model flat,stdcall ; uses flat memory addressing model
option casemap:none

include C:\masm32\include\windows.inc   ; windows.inc have structures and constants
include C:\masm32\include\user32.inc
includelib C:\masm32\lib\user32.lib ; CreateWindowEx, RegisterClassEx,...
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib   ; ExitProcess
include C:\masm32\include\masm32.inc
includelib C:\masm32\lib\masm32.lib
include C:\masm32\include\gdi32.inc
includelib C:\masm32\lib\gdi32.lib

.CONST
DRAWING equ 1
WAITING equ 0
PEN_COLOR equ 00000000h ; black
PEN_SIZE equ 2
BALL_SIZE equ 35
BALL_SPEED equ 20

.DATA
ClassName db 'SimpleWinClass',0
AppName db 'Ball',0

state db WAITING

vectorX dd 19
vectorY dd -19

WIN_WIDTH dd 700
WIN_HEIGHT dd 500

.DATA?
; HINSTANCE & LPSTR typedef DWORD in windows.inc
; reserve the space for future use
hInstance HINSTANCE ?

tlpoint POINT <>
brpoint POINT <>

; use for create window
wc WNDCLASSEX <?>
msg MSG <?> ; handle message
hwnd HWND ? ; handle window procedure

hdc HDC ?
ps PAINTSTRUCT <?>

time SYSTEMTIME <?>

hPen HPEN ?

.CODE
start:
    ; call GetModuleHandle(null)
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx
    push NULL
    call GetModuleHandle    ; module handle same as instance handle in Win32
    mov hInstance, eax  ; return an instance to handle in eax

    ; call WinMain(hInstance, hPrevInstance, CmdLine, CmdShow)
    ; our main function
    push SW_SHOW
    push NULL
    push NULL
    push hInstance
    call WinMain

    ; call ExitProcess
    push eax
    call ExitProcess

    ; Define WinMain
    WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
        ; Structure in msdn, define in windows.inc
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx

        ; Load default icon
        push IDI_APPLICATION
        push NULL
        call LoadIcon
        mov wc.hIcon, eax
        mov wc.hIconSm, eax

        ; Load default cursor
        push IDC_ARROW
        push NULL
        call LoadCursor
        mov wc.hCursor, eax

        mov wc.cbSize, SIZEOF WNDCLASSEX    ; size of this structure
        mov wc.style, CS_HREDRAW or CS_VREDRAW  ; style of windows https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
        mov wc.lpfnWndProc, OFFSET WndProc  ; andress of window procedure
        mov wc.cbClsExtra, NULL
        mov wc.cbWndExtra, NULL
        push hInstance
        pop wc.hInstance
        mov wc.hbrBackground, COLOR_WINDOW+1    ; background color, require to add 1
        mov wc.lpszMenuName, NULL
        mov wc.lpszClassName, OFFSET ClassName

        ; we register our own class, named in ClassName
        push offset wc
        call RegisterClassEx

        ; after register ClassName, we use it to create windows compond
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
        push NULL
        push hInstance
        push NULL
        push NULL
        push WIN_HEIGHT
        push WIN_WIDTH
        push CW_USEDEFAULT
        push CW_USEDEFAULT
        push WS_OVERLAPPEDWINDOW
        push offset AppName
        push offset ClassName
        push WS_EX_CLIENTEDGE
        call CreateWindowEx

        mov hwnd, eax   ; return windows handle

        ; display window
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
        push CmdShow
        push hwnd
        call ShowWindow

        ; update window
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/dd145167(v=vs.85).aspx
        push hwnd
        call UpdateWindow

        ; Message Loop
        MESSAGE_LOOP:
            ; get message
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
            push 0
            push 0
            push NULL
            push offset msg
            call GetMessage

            ; return in eax
            ; if the function retrieves a message other than WM_QUIT, the return value is nonzero.
            ; if the function retrieves the WM_QUIT message, the return value is zero.
            test eax, eax
            jle END_LOOP

            ; translate virtual-key messages into character messages - ASCII in WM_CHAR
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955(v=vs.85).aspx
            push offset msg
            call TranslateMessage

            ; sends the message data to the window procedure responsible for the specific window the message is for.
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms644934(v=vs.85).aspx
            push offset msg
            call DispatchMessage

            jmp MESSAGE_LOOP

        END_LOOP:
            mov eax, msg.wParam
        ret
    WinMain endp

    TimerProc PROC thwnd:HWND, uMsg:UINT, idEvent:UINT, dwTime:DWORD
            push TRUE
            push NULL
            push thwnd
            call InvalidateRect
            ret
    TimerProc ENDP

    ; Handle message with switch(notification)
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
    WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
        cmp uMsg, WM_PAINT
        je ON_WM_PAINT

        cmp uMsg, WM_CREATE
        je ON_WM_CREATE

        cmp uMsg, WM_LBUTTONDOWN
        je ON_WM_LBUTTONDOWN

        cmp uMsg, WM_DESTROY
        je ON_WM_DESTROY

        cmp uMsg, WM_QUIT
        je ON_WM_DESTROY

        cmp uMsg, WM_CLOSE
        je ON_WM_DESTROY

        jmp ON_DEFAULT

        ; user close program
        ON_WM_DESTROY:
            push NULL
            call PostQuitMessage
            jmp EXIT

        ON_WM_CREATE:
            ; create a pen with specific color and size
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/dd183509(v=vs.85).aspx
            push PEN_COLOR
            push PEN_SIZE
            push PS_SOLID
            call CreatePen
            mov hPen, eax

            jmp EXIT

        ON_WM_LBUTTONDOWN:
            cmp [state], DRAWING
            je EXIT

            push lParam
            call updateXY

            ; when clicked, set state to DRAWING
            mov [state], DRAWING

            push OFFSET TimerProc
            push 20
            push 1
            push hwnd
            call SetTimer

;            mov dword ptr[time.wMilliseconds], BALL_SPEED
;            push offset time
;            call SetSystemTime

            jmp EXIT

        ON_WM_PAINT:
            mov dword ptr[time.wMilliseconds], 0

            push offset ps
            push hWnd
            call BeginPaint
            mov hdc, eax

            ; apply pen to hdc
            push hPen
            push hdc
            call SelectObject

            call createEllipse

            push offset ps
            push hWnd
            call EndPaint

            jmp EXIT

        ON_DEFAULT:
            ; handle any message that program don't handle
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
            push lParam
            push wParam
            push uMsg   ; message
            push hWnd   ; windows
            call DefWindowProc

            jmp EXIT

        EXIT:
            ret
    WndProc endp

    createEllipse proc
        push brpoint.y
        push brpoint.x
        push tlpoint.y
        push tlpoint.x
        push hdc
        call Ellipse

        call moveEllipse

        mov eax, WIN_WIDTH
        cmp brpoint.x, eax
        jg MEET_RIGHT_LEFT

        mov eax, WIN_HEIGHT
        cmp brpoint.y, eax
        jg MEET_BOTTOM_TOP

        cmp tlpoint.x, 0
        jl MEET_RIGHT_LEFT

        cmp tlpoint.y, 0
        jl MEET_BOTTOM_TOP

        jmp MEET_NONE

        MEET_RIGHT_LEFT:
            neg vectorX
            jmp MEET_NONE

        MEET_BOTTOM_TOP:
            neg vectorY
            jmp MEET_NONE

        MEET_NONE:

        ret
    createEllipse endp

    moveEllipse proc
        mov eax, dword ptr[vectorX]
        mov ecx, dword ptr[vectorY]

        add tlpoint.x, eax
        add tlpoint.y, ecx
        add brpoint.x, eax
        add brpoint.y, ecx

        ret
    moveEllipse endp

    updateXY proc lParam:LPARAM
        mov eax, lParam

        ; get low word that contain x
        xor ebx, ebx
        mov bx, ax

        mov tlpoint.x, ebx
        mov brpoint.x, ebx
        add brpoint.x, BALL_SIZE

        ; get high word that contain y
        mov eax, lParam
        shr eax, 16

        mov tlpoint.y, eax
        mov brpoint.y, eax
        add brpoint.y, BALL_SIZE

        ret
    updateXY endp

end start