:從零搭建游戲循環(huán)與事件處理)
1. 為什么我不建議一上來就啃游戲引擎很多人想學(xué)游戲開發(fā)第一反應(yīng)是去下載那些功能齊全的大型引擎結(jié)果裝完打開界面就懵了——滿屏的面板、屬性、節(jié)點樹光是搞清楚哪個窗口對應(yīng)哪個功能就要花掉一整個周末。我見過太多人卡在這一步熱情還沒點燃就被澆滅了。PyGame 走的是完全相反的路線。它本質(zhì)上就是一組幫你在窗口里畫圖、放聲音、接收鍵盤鼠標(biāo)輸入的 Python 模塊沒有編輯器沒有可視化界面你寫的每一行代碼都直接對應(yīng)屏幕上發(fā)生的事。這種“裸奔”式的開發(fā)體驗恰恰是理解游戲循環(huán)、幀率控制、碰撞檢測這些底層概念的最佳途徑。這篇文章適合誰如果你已經(jīng)會寫 Python 的基本語法——知道列表、字典、循環(huán)、函數(shù)怎么用——但從來沒做過任何圖形界面程序那這篇就是為你準(zhǔn)備的。我會從零開始把 PyGame 的安裝、窗口創(chuàng)建、事件處理、畫面繪制、幀率控制這幾個核心環(huán)節(jié)全部拆開講清楚每一步都告訴你為什么這么做以及我實際踩過的坑。需要提前說明的是PyGame 并不是只能做小游戲。它的 2D 渲染能力足夠支撐中等規(guī)模的獨立游戲很多在游戲平臺上架的像素風(fēng)作品就是用 PyGame 或其衍生庫完成的。但它的定位始終是“讓你專注于游戲邏輯本身”而不是幫你自動生成場景和物理效果。理解這一點你后面的學(xué)習(xí)路徑會清晰很多。2. 環(huán)境搭建少裝一個包都可能讓你卡半天2.1 Python 版本的選擇不是越新越好PyGame 對 Python 版本有明確的兼容范圍。截至我寫這篇文章的時候PyGame 2.x 系列穩(wěn)定支持 Python 3.8 到 3.12。如果你用的是 Python 3.13 或更高版本可能會遇到預(yù)編譯包還沒跟上的情況安裝時直接報編譯錯誤。我的建議是用 Python 3.10 或 3.11。這兩個版本既不太老能享受現(xiàn)代 Python 的語法特性又足夠穩(wěn)定PyGame 的輪子包wheel覆蓋完整安裝時不需要本地編譯。如果你電腦上已經(jīng)裝了其他版本可以用虛擬環(huán)境來隔離避免把系統(tǒng)環(huán)境搞亂。python -m venv pygame_env # Windows pygame_env\Scripts\activate # macOS / Linux source pygame_env/bin/activate虛擬環(huán)境的好處是你在這個環(huán)境里裝的 PyGame 不會影響其他項目。萬一裝崩了直接刪掉整個文件夾重新來就行不用重裝 Python。2.2 安裝 PyGame 時最容易忽略的細(xì)節(jié)安裝命令本身很簡單pip install pygame但這里有幾個坑我必須要提醒你。第一個坑是pip 版本過舊。有些系統(tǒng)自帶的 pip 版本比較老解析依賴時可能拉不到最新的 PyGame 包。先跑一下pip install --upgrade pip再裝 PyGame能省掉很多莫名其妙的報錯。第二個坑是網(wǎng)絡(luò)問題導(dǎo)致的超時。PyGame 的包大概十幾兆如果網(wǎng)絡(luò)不穩(wěn)定下載到一半斷了pip 可能會留下一個損壞的緩存。這時候你會看到類似“ERROR: Could not install packages due to an OSError”的提示。解決辦法是加上超時參數(shù)并清緩存pip install pygame --timeout 120 --no-cache-dir第三個坑是macOS 上的架構(gòu)問題。如果你用的是 M 系列芯片的 Mac確保你的 Python 也是 arm64 版本。有些通過某些方式安裝的 Python 是 x86_64 架構(gòu)裝 PyGame 時雖然能裝上但運行時會報“mach-o file, but is an incompatible architecture”的錯誤。檢查方法是在終端輸入python -c import platform; print(platform.machine())輸出應(yīng)該是arm64。裝完之后用下面這行代碼驗證是否成功import pygame print(pygame.version.ver)如果輸出了版本號比如2.5.2說明安裝沒問題。如果報ModuleNotFoundError那多半是裝到了另一個 Python 環(huán)境里檢查一下你的虛擬環(huán)境是否激活。2.3 開發(fā)工具的選擇別在這件事上糾結(jié)太久寫 PyGame 代碼用什么編輯器都行。VS Code、PyCharm、甚至記事本加命令行都可以。我個人的習(xí)慣是 VS Code 加 Python 插件輕量、啟動快調(diào)試功能也夠用。唯一需要注意的是確保你的編輯器使用的 Python 解釋器和你安裝 PyGame 的那個環(huán)境是同一個。VS Code 里按CtrlShiftP輸入“Python: Select Interpreter”選你剛才創(chuàng)建的虛擬環(huán)境。PyCharm 則在項目設(shè)置里指定解釋器路徑。這個細(xì)節(jié)看起來不起眼但新手最容易在這里翻車——明明裝了 PyGame編輯器里卻一直報“模塊未找到”。3. 第一個窗口從黑屏到能關(guān)閉只差一個事件循環(huán)3.1 最小可運行代碼的每一行都在干什么先看一段能跑起來的最簡代碼然后我逐行拆解import pygame import sys pygame.init() screen pygame.display.set_mode((800, 600)) pygame.display.set_caption(我的第一個 PyGame 窗口) running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False pygame.quit() sys.exit()pygame.init()做的事情比你想的多。它會初始化 PyGame 的所有子模塊——顯示、聲音、字體、手柄等等。如果你確定不需要某些模塊比如聲音可以單獨初始化但在入門階段直接全量初始化最省事。set_mode((800, 600))創(chuàng)建了一個 800 像素寬、600 像素高的窗口。這個函數(shù)返回的是一個 Surface 對象你可以把它理解成一塊畫布。后面所有的繪制操作要么畫在這塊畫布上要么畫在從它派生出來的其他 Surface 上。pygame.display.set_caption()設(shè)置窗口標(biāo)題欄的文字。這個不是必須的但不設(shè)的話窗口標(biāo)題會顯示為“pygame window”看起來很不專業(yè)。事件循環(huán)是整個游戲的心臟。pygame.event.get()會返回一個列表里面是自上次調(diào)用以來發(fā)生的所有事件——用戶點了關(guān)閉按鈕、按了鍵盤、動了鼠標(biāo)、調(diào)整了窗口大小等等。如果你不處理這些事件操作系統(tǒng)會認(rèn)為你的程序卡死了窗口會變成“無響應(yīng)”狀態(tài)。pygame.QUIT事件專門對應(yīng)點擊窗口關(guān)閉按鈕這個動作。捕獲到它之后把running設(shè)為False循環(huán)結(jié)束程序退出。3.2 為什么你的窗口一打開就卡死新手最常遇到的問題是窗口出來了但點關(guān)閉按鈕沒反應(yīng)整個窗口白屏標(biāo)題欄顯示“無響應(yīng)”。原因幾乎總是同一個你沒有在循環(huán)里調(diào)用pygame.event.get()。操作系統(tǒng)會不斷向你的程序發(fā)送各種事件消息如果你的程序不讀取這些消息消息隊列就會堆積系統(tǒng)判定程序失去響應(yīng)。另一個常見原因是在循環(huán)里寫了死循環(huán)或耗時操作。比如你在主循環(huán)里放了一個while True去等待某個條件那事件就永遠(yuǎn)沒機(jī)會被處理。記住一個原則主循環(huán)的每一輪迭代都必須盡快完成把控制權(quán)交還給事件系統(tǒng)。還有一個隱蔽的坑在某些系統(tǒng)上窗口創(chuàng)建后需要調(diào)用一次pygame.event.pump()或處理一次事件窗口才會真正顯示出來。如果你發(fā)現(xiàn)窗口創(chuàng)建了但內(nèi)容不刷新可以在主循環(huán)之前加一句pygame.event.pump()試試。3.3 幀率控制不限速的循環(huán)會吃掉整個 CPU上面的最小代碼有一個嚴(yán)重問題while running循環(huán)會以 CPU 能跑的最快速度瘋狂執(zhí)行一秒可能跑幾百萬次。這不僅浪費電還會讓你的電腦風(fēng)扇狂轉(zhuǎn)。解決辦法是加一個時鐘對象來控制幀率clock pygame.time.Clock() while running: # ... 事件處理 ... # ... 繪制操作 ... clock.tick(60)clock.tick(60)的意思是確保這一輪循環(huán)距離上一輪循環(huán)至少間隔 1/60 秒。如果這一輪跑得快它就主動等待一會兒如果跑得慢它就不等待直接進(jìn)入下一輪。這樣游戲的邏輯更新和畫面刷新就穩(wěn)定在每秒 60 次。為什么是 60因為大多數(shù)顯示器的刷新率是 60Hz每秒 60 幀正好和顯示器同步畫面看起來最流暢。如果你的顯示器是 144Hz 的可以設(shè)成 144但對入門項目來說 60 完全夠用。注意clock.tick()的返回值是上一輪循環(huán)實際消耗的毫秒數(shù)。如果你要做基于時間的動畫比如“每秒移動 100 像素”需要用這個值來計算位移而不是假設(shè)每幀固定移動多少像素。這個細(xì)節(jié)后面講動畫時會展開。4. 畫面繪制從填充背景到畫出會動的方塊4.1 Surface 和 RectPyGame 里最重要的兩個概念PyGame 里所有的可見元素都是Surface。窗口本身是一個 Surface你加載的圖片是一個 Surface甚至文字渲染出來也是一個 Surface。Surface 有寬度、高度、像素格式等屬性你可以對它進(jìn)行填充、繪制、旋轉(zhuǎn)、縮放等操作。Rect是另一個核心概念它表示一個矩形區(qū)域有 x、y、width、height 四個屬性。Rect 本身不顯示任何東西但它用來定位 Surface、檢測碰撞、定義繪制區(qū)域。這兩個概念的關(guān)系可以這樣理解Surface 是“畫”Rect 是“畫框”。你把畫放進(jìn)畫框里再把畫框放到窗口的某個位置。創(chuàng)建一個 Rect 的常見方式rect pygame.Rect(100, 200, 50, 50)這表示一個左上角在 (100, 200)、寬 50、高 50 的矩形。PyGame 的坐標(biāo)系原點在窗口左上角x 向右增大y 向下增大。這一點和數(shù)學(xué)課上學(xué)的笛卡爾坐標(biāo)系不同剛開始可能會不習(xí)慣但記住“y 往下是正方向”就行了。4.2 填充背景與繪制基本圖形每一幀的開始你通常需要先用一個顏色填充整個窗口把上一幀的內(nèi)容擦掉。這叫做“清屏”。screen.fill((30, 30, 30)) # 深灰色背景顏色用 RGB 元組表示每個分量的范圍是 0 到 255。(0, 0, 0)是黑色(255, 255, 255)是白色(255, 0, 0)是紅色。畫一個矩形pygame.draw.rect(screen, (0, 200, 100), (100, 200, 50, 50))第一個參數(shù)是目標(biāo) Surface第二個是顏色第三個是矩形的位置和大小。還可以加第四個參數(shù)指定線寬如果不加或者設(shè)為 0就是實心填充。畫圓pygame.draw.circle(screen, (200, 100, 0), (400, 300), 40)第三個參數(shù)是圓心坐標(biāo)第四個是半徑。畫線pygame.draw.line(screen, (255, 255, 255), (0, 0), (800, 600), 3)最后那個 3 是線寬。這些繪制函數(shù)都有一個共同點它們直接修改目標(biāo) Surface 的像素數(shù)據(jù)不會返回任何東西。所以你不需要接收返回值調(diào)用完就生效了。4.3 雙緩沖與 flip為什么畫面會閃爍如果你直接在窗口的 Surface 上繪制然后立刻顯示可能會看到畫面閃爍。這是因為繪制過程不是瞬間完成的顯示器在繪制到一半的時候刷新了畫面就會看到撕裂或閃爍。PyGame 的解決方案是雙緩沖。set_mode()創(chuàng)建的窗口默認(rèn)就是雙緩沖的你繪制的內(nèi)容先寫到一個隱藏的后臺緩沖區(qū)等你調(diào)用pygame.display.flip()或pygame.display.update()時整個后臺緩沖區(qū)一次性交換到前臺顯示。# 每一幀的繪制流程 screen.fill((30, 30, 30)) pygame.draw.rect(screen, (0, 200, 100), (100, 200, 50, 50)) pygame.display.flip()flip()和update()的區(qū)別是flip()更新整個窗口update()可以只更新指定的矩形區(qū)域。對于小項目直接用flip()最省心。如果你要做局部刷新優(yōu)化可以傳一個矩形列表給update()只刷新變化的部分。實測經(jīng)驗在某些 Linux 桌面環(huán)境下如果不在每幀末尾調(diào)用flip()或update()窗口可能完全不顯示內(nèi)容。養(yǎng)成“繪制完就 flip”的習(xí)慣能避免很多平臺差異問題。4.4 讓方塊動起來位置更新與幀率的關(guān)系靜態(tài)畫面沒什么意思讓方塊動起來只需要在每一幀改變它的位置x 100 speed 5 while running: # ... 事件處理 ... screen.fill((30, 30, 30)) x speed if x 800: x -50 pygame.draw.rect(screen, (0, 200, 100), (x, 200, 50, 50)) pygame.display.flip() clock.tick(60)這段代碼讓方塊以每幀 5 像素的速度向右移動超出右邊界后從左邊重新出現(xiàn)。在 60 幀每秒的情況下實際速度是 300 像素每秒。但這里有一個問題如果某一幀因為系統(tǒng)卡頓耗時較長方塊會“跳”一大段距離。對于入門項目這種誤差可以接受。但如果你要做精確的物理模擬或計時就需要用clock.tick()的返回值來計算基于時間的位移dt clock.tick(60) / 1000.0 # 轉(zhuǎn)換為秒 x speed_per_second * dt這樣無論幀率如何波動方塊每秒移動的距離都是恒定的。這個技巧在后面的進(jìn)階內(nèi)容里會反復(fù)用到。5. 事件處理進(jìn)階鍵盤、鼠標(biāo)與持續(xù)按鍵5.1 事件類型與事件對象的結(jié)構(gòu)PyGame 的事件系統(tǒng)基于一個隊列。每次調(diào)用pygame.event.get()你拿到的是自上次調(diào)用以來所有事件的列表。每個事件是一個對象有type屬性表示事件類型還有其他屬性根據(jù)類型不同而不同。常見的事件類型事件類型觸發(fā)時機(jī)關(guān)鍵屬性QUIT點擊窗口關(guān)閉按鈕無KEYDOWN按下鍵盤按鍵key,mod,unicodeKEYUP松開鍵盤按鍵key,modMOUSEBUTTONDOWN按下鼠標(biāo)按鈕pos,buttonMOUSEBUTTONUP松開鼠標(biāo)按鈕pos,buttonMOUSEMOTION移動鼠標(biāo)pos,rel,buttons處理鍵盤事件的基本寫法for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: if event.key pygame.K_SPACE: print(空格鍵被按下) elif event.key pygame.K_ESCAPE: running Falsepygame.K_SPACE、pygame.K_ESCAPE這些是 PyGame 預(yù)定義的鍵碼常量。用常量而不是數(shù)字代碼可讀性更好也不容易出錯。5.2 持續(xù)按鍵的正確處理方式KEYDOWN事件只在按下的那一瞬間觸發(fā)一次。如果你想讓角色在按住方向鍵時持續(xù)移動不能只靠KEYDOWN事件因為操作系統(tǒng)在按住鍵不放時會先有一個短暫的延遲然后才開始重復(fù)發(fā)送KEYDOWN事件而且重復(fù)頻率是可以設(shè)置的不同系統(tǒng)不一樣。正確的做法是用pygame.key.get_pressed()獲取當(dāng)前所有按鍵的按下狀態(tài)。keys pygame.key.get_pressed() if keys[pygame.K_LEFT]: x - speed if keys[pygame.K_RIGHT]: x speed if keys[pygame.K_UP]: y - speed if keys[pygame.K_DOWN]: y speedget_pressed()返回一個布爾序列索引是鍵碼值是True表示當(dāng)前被按住。這個函數(shù)應(yīng)該在每一幀的事件處理之后、繪制之前調(diào)用這樣角色的移動才能和當(dāng)前按鍵狀態(tài)同步。注意get_pressed()返回的狀態(tài)是“當(dāng)前這一刻”的快照。如果你在事件循環(huán)內(nèi)部調(diào)用它可能拿到的是舊狀態(tài)。把它放在主循環(huán)里、事件處理之后是最穩(wěn)妥的位置。5.3 鼠標(biāo)交互點擊、移動與按鈕狀態(tài)鼠標(biāo)事件的處理和鍵盤類似但多了一個位置信息elif event.type pygame.MOUSEBUTTONDOWN: if event.button 1: # 左鍵 print(f左鍵點擊在 {event.pos}) elif event.button 3: # 右鍵 print(f右鍵點擊在 {event.pos})event.pos是一個(x, y)元組表示點擊位置相對于窗口左上角的坐標(biāo)。鼠標(biāo)移動事件MOUSEMOTION會非常頻繁地觸發(fā)如果你在里面做復(fù)雜計算可能會拖慢幀率。一個常見的優(yōu)化是只在需要的時候才處理鼠標(biāo)移動比如拖拽操作。elif event.type pygame.MOUSEMOTION: if dragging: rect.x event.pos[0] - offset_x rect.y event.pos[1] - offset_y這里的dragging是一個布爾標(biāo)志在MOUSEBUTTONDOWN時設(shè)為True在MOUSEBUTTONUP時設(shè)為False。offset_x和offset_y記錄點擊位置相對于矩形左上角的偏移這樣拖拽時矩形不會“跳”到鼠標(biāo)位置。5.4 事件處理的常見誤區(qū)第一個誤區(qū)是在事件循環(huán)里做耗時操作。比如在KEYDOWN里加載一張大圖或播放一段長音頻這會導(dǎo)致事件隊列堆積窗口卡頓。正確的做法是設(shè)置一個標(biāo)志在主循環(huán)的更新階段再執(zhí)行耗時操作。第二個誤區(qū)是忘記處理QUIT事件。有些教程的示例代碼只處理鍵盤事件結(jié)果用戶點關(guān)閉按鈕沒反應(yīng)。無論你的游戲多簡單QUIT事件都必須處理。第三個誤區(qū)是混淆event.key和event.unicode。event.key是鍵碼表示按的是哪個物理鍵event.unicode是輸入的字符受鍵盤布局和修飾鍵影響。如果你要做文本輸入用event.unicode如果你要做游戲控制用event.key。6. 從能跑到好用我的調(diào)試與優(yōu)化心得6.1 用 print 調(diào)試游戲循環(huán)的陷阱在游戲循環(huán)里加print是最直接的調(diào)試方式但有一個陷阱每幀都打印會導(dǎo)致輸出刷屏而且 print 本身會拖慢循環(huán)速度讓你誤以為游戲性能有問題。更好的做法是只在特定條件滿足時打印或者用計數(shù)器控制打印頻率。frame_count 0 while running: # ... frame_count 1 if frame_count % 60 0: print(f當(dāng)前幀: {frame_count}, 方塊位置: {x})這樣每 60 幀才打印一次既能看到狀態(tài)變化又不會刷屏。另一個技巧是把調(diào)試信息畫在窗口上而不是打印到控制臺。PyGame 的字體模塊可以渲染文字到 Surfacefont pygame.font.SysFont(None, 24) text font.render(fFPS: {clock.get_fps():.1f}, True, (255, 255, 255)) screen.blit(text, (10, 10))clock.get_fps()返回最近一段時間的平均幀率。把這個數(shù)字顯示在窗口角落你能直觀地看到性能變化。6.2 資源加載的時機(jī)與路徑問題圖片、聲音、字體這些資源應(yīng)該在主循環(huán)之前加載完畢而不是在循環(huán)內(nèi)部反復(fù)加載。加載一次反復(fù)使用這是基本原則。路徑問題是新手最容易踩的坑。如果你用相對路徑加載圖片image pygame.image.load(player.png)程序運行時的工作目錄決定了這個路徑相對于哪里。如果你在命令行里從不同目錄啟動程序或者把程序打包成可執(zhí)行文件相對路徑就會失效。穩(wěn)妥的做法是用os.path構(gòu)造絕對路徑import os base_path os.path.dirname(os.path.abspath(__file__)) image_path os.path.join(base_path, assets, player.png) image pygame.image.load(image_path)__file__是當(dāng)前腳本的文件路徑os.path.dirname取它所在的目錄然后拼上資源子目錄和文件名。這樣無論從哪里啟動程序路徑都是正確的。6.3 性能優(yōu)化的幾個實用手段PyGame 的性能瓶頸通常出現(xiàn)在兩個地方繪制調(diào)用次數(shù)過多和Surface 轉(zhuǎn)換不當(dāng)。如果你要繪制大量相同的圖片不要每次都blit原始圖片。先把圖片用convert()或convert_alpha()轉(zhuǎn)換成窗口的像素格式image pygame.image.load(player.png).convert_alpha()convert_alpha()保留透明度convert()不保留。轉(zhuǎn)換之后PyGame 在 blit 時不需要做像素格式轉(zhuǎn)換速度會快很多。這個優(yōu)化在繪制幾十個精靈時效果不明顯但繪制幾百個時差距就出來了。另一個手段是減少每幀的填充區(qū)域。如果你知道只有一小塊區(qū)域發(fā)生了變化可以用screen.fill(color, rect)只填充那塊區(qū)域而不是整個窗口。不過對于 800x600 這種小窗口全屏填充的開銷可以忽略這個優(yōu)化留到窗口更大時再考慮。還有一個容易忽略的點避免在循環(huán)里創(chuàng)建新的 Surface 或字體對象。創(chuàng)建對象是有開銷的每幀創(chuàng)建幾百個對象會讓垃圾回收器頻繁工作。把創(chuàng)建操作移到循環(huán)外面循環(huán)里只做 blit。6.4 我踩過的三個典型坑第一個坑在 Windows 上窗口標(biāo)題中文亂碼。set_caption()傳入中文字符串時某些 PyGame 版本在 Windows 上會顯示亂碼。解決辦法是確保源文件用 UTF-8 編碼并且在文件開頭加上# -*- coding: utf-8 -*-。如果還是不行可以嘗試用pygame.display.set_caption()的字節(jié)串形式但更推薦升級到最新版 PyGame新版本對 Unicode 的支持已經(jīng)好很多了。第二個坑退出時程序卡住不結(jié)束。有時候pygame.quit()之后程序還在運行需要手動結(jié)束進(jìn)程。這通常是因為還有未處理的線程或未釋放的資源。確保在pygame.quit()之后調(diào)用sys.exit()并且不要在退出前留下未關(guān)閉的文件或網(wǎng)絡(luò)連接。第三個坑在不同電腦上幀率差異巨大。同樣的代碼在你的電腦上跑 60 幀在別人的電腦上只有 20 幀。除了硬件差異還要檢查是否開啟了垂直同步。set_mode()可以傳vsync1參數(shù)來開啟垂直同步但這在某些驅(qū)動上會導(dǎo)致幀率被鎖定在顯示器刷新率。如果你要做性能測試先關(guān)掉垂直同步。7. 下一步可以往哪里走窗口能開了方塊能動了鍵盤鼠標(biāo)能響應(yīng)了這已經(jīng)是一個完整游戲循環(huán)的雛形。接下來你可以往幾個方向擴(kuò)展。精靈和精靈組是 PyGame 提供的一個組織游戲?qū)ο蟮臋C(jī)制。把每個游戲角色封裝成Sprite子類用Group來批量更新和繪制代碼會清爽很多。這是從“能跑”到“好維護(hù)”的關(guān)鍵一步。碰撞檢測是游戲邏輯的核心。PyGame 的Rect自帶colliderect()方法可以檢測兩個矩形是否重疊。對于像素級精確碰撞可以用pygame.sprite.collide_mask()但性能開銷更大入門階段用矩形碰撞就夠了。聲音和音樂方面pygame.mixer模塊可以播放音效和背景音樂。注意音效和音樂要分開管理音效用Sound對象音樂用music模塊兩者的加載和播放方式略有不同。打包發(fā)布是最后一步。pyinstaller可以把你的 Python 腳本打包成可執(zhí)行文件但 PyGame 的資源文件需要額外配置確保打包后程序能找到圖片和聲音。這個環(huán)節(jié)坑比較多建議等游戲基本完成后再折騰。如果你跟著這篇文章把代碼都敲了一遍現(xiàn)在應(yīng)該有一個能響應(yīng)鍵盤、能繪制圖形、幀率穩(wěn)定的窗口程序了。這個基礎(chǔ)打好了后面學(xué)任何游戲開發(fā)框架都會輕松很多因為游戲循環(huán)、事件驅(qū)動、幀率控制這些概念是相通的。