
入門 Python 一定要做的小項目貪吃蛇游戲從 0 到 1 完整實現很多 Python 初學者都會遇到一個典型困境語法書看了兩遍變量、列表、循環、函數似乎都懂了但一離開教程還是不知道這些知識到底能做什么。去刷算法題感覺和日常開發離得太遠直接上手 Web 后端又被框架、數據庫、部署工具勸退。這時候一個能跑在本地窗口里的小游戲反而是性價比最高的練手方式。貪吃蛇就是這樣一個幾乎完美的 Python 練手項目。它小整個游戲核心邏輯可能只需要一兩百行代碼但它完整涉及窗口創建、事件處理、游戲循環、碰撞檢測、狀態管理、分數計分這些恰恰是寫任何圖形界面程序、任何游戲、甚至很多后臺服務都會遇到的基礎套路。更重要的是完成之后你可以直接運行、演示、改功能正反饋非常強。這篇文章會用 Python 和 Pygame 從零實現一個可運行的貪吃蛇小游戲重點不是讓你抄一段代碼就跑而是把每個環節拆開講清楚蛇為什么用列表表示、方向按鍵為什么不能立刻反向、碰撞檢測怎么做、格子化移動是怎么回事。看完之后你可以自己動手擴展出關卡、暫停、重新開始等功能也會具備讀懂大多數 Pygame 小游戲源碼的能力。整篇文章會覆蓋環境準備、核心原理、完整代碼、運行驗證、常見問題、工程優化建議六個部分。代碼可以直接復制運行建議你邊看邊敲遇到報錯先自己嘗試定位再對照文中的排查表。1. 為什么貪吃蛇是 Python 練手項目的經典選擇很多人以為貪吃蛇只是個“小游戲”沒什么技術含量。這個判斷不全面。它確實小但它的“小”是麻雀雖小五臟俱全正好覆蓋了 Python 初學者從語法學習走向小型項目實踐時要跨過的那道坎。1.1 它幫初學者解決了什么問題剛開始學 Python 時很多人最大的困惑不是某個語法記不住而是不知道“這些東西組合在一起能干什么”。列表、字典、循環、函數、條件判斷每一個單獨拿出來都不難但組合成一個可以交互的程序就需要一種叫“抽象思維”的能力。貪吃蛇恰恰能逼著你去想蛇身用一整條數據結構怎么表示吃食物時蛇變長體現到代碼里到底是什么操作撞墻和撞自己在坐標層面分別怎么判斷這些思考過程就是真正編程能力形成的開始。你不需要在腦中憑空想象一個系統只需要面對一個簡單規則的游戲卻能把“狀態變化”這件事想得清清楚楚。1.2 貪吃蛇涉及的知識點覆蓋面貪吃蛇用 Pygame 實現后會接觸以下核心知識點坐標系統窗口是二維平面所有物體用 (x, y) 坐標描述。列表操作蛇身使用列表存儲坐標點移動時頭部加入新坐標、尾部移除舊坐標。事件循環監聽鍵盤按鍵、窗口關閉等事件。狀態判斷游戲進行中、暫停、結束三個狀態的切換。while 循環和幀率控制游戲循環不斷刷新畫面FPS 決定速度。模塊化思維把初始化、繪制、移動、碰撞檢測、重新開始等功能拆成函數或類。這些知識點在后面學習 Pygame 游戲開發、Tkinter 桌面應用、Pyglet 或 Panda3D 等更復雜圖形庫甚至學習數據處理時都會以不同形式再次出現。1.3 為什么選擇 PygamePython 里寫小游戲有幾個可選方案分別是 turtle、tkinter、pygame、pyglet。Turle 更適合剛接觸編程的青少年它偏重繪圖教學tkinter 是 Python 自帶的 GUI 庫能做一些簡單交互應用但做游戲的視覺反饋和事件處理不夠順手pygame 是專門做 2D 游戲的多媒體庫提供了游戲循環、事件、圖像、聲音、字體、碰撞檢測等模塊是目前學習 Python 小游戲最主流的方案。Pygame 的學習曲線不算陡峭安裝一條命令就能完成API 風格也比較直接。用 Pygame 寫貪吃蛇可以不用擔心底層窗口創建的細節把注意力集中在游戲邏輯本身這對初學者特別友好。2. 貪吃蛇核心概念與實現原理2.1 格子化移動與坐標體系貪吃蛇游戲中最容易讓新手暈的地方是蛇的移動到底怎么表現。很多人的第一反應是“蛇應該是一格一格前進動畫效果順滑一點更好”但在最簡實現里蛇的移動是離散跳躍的也就是每隔一定時間整個蛇身狀態變化一次蛇頭坐標改變一個格子單位的距離蛇身跟隨移動。為了讓坐標計算簡單游戲窗口通常被劃分成固定大小的格子。比如窗口寬度是 480每個格子是 20 像素那么橫向就有 24 個格子。蛇的所有坐標值都是格子大小的整數倍比如 (80, 60)、(100, 60)這樣移動時只需要加或減 20不會出現蛇身停在兩個格子中間這種不符合游戲規則的情況。這種格子化的設計帶來了一個隱藏的好處碰撞檢測非常直接。判斷蛇是否吃到食物只需要比較蛇頭坐標和食物坐標是否相等判斷蛇是否撞墻只需要檢查蛇頭坐標是否超出窗口邊界判斷蛇是否撞到自己只需要檢查蛇頭坐標是否出現在蛇身列表中。2.2 蛇身為什么用列表表示蛇身可以用一個列表保存列表的每個元素是一個坐標元組 (x, y)。列表的第一個元素是蛇頭最后一個元素是蛇尾。蛇向右移動一格時只需要把新蛇頭坐標 insert 到列表頭部同時刪除列表尾部的最后一個坐標。這個操作的邏輯和現實中蛇的移動完全一致頭部延伸出去一格尾部縮回來一格整條蛇看起來向前移動了一格。如果吃到了食物只插入新蛇頭坐標、不刪除蛇尾坐標蛇身長度就加一。Pygame 里繪制蛇的時候遍歷蛇身列表把每個坐標對應的矩形畫出來即可。列表長度、繪制數量、碰撞檢測都圍繞同一個數據結構展開這讓代碼非常清晰。用鏈表也能實現蛇身但對貪吃蛇這種長度不大、只在尾部刪除的游戲場景Python 列表的性能完全夠用代碼可讀性反而更好。2.3 方向控制與禁止直接反向方向控制是貪吃蛇最容易出 bug 的地方之一。蛇向右移動時如果玩家按下左方向鍵蛇會瞬間向左然后直接穿過自己的身體。這不符合游戲規則因為蛇只能轉向 90 度而不能調轉 180 度。因此在按鍵事件處理時需要做方向合法性校驗如果蛇頭當前向右那么玩家按左鍵時忽略這次輸入按上鍵或下鍵時允許轉向。一種常見寫法是把方向用向量表示比如向右是 (1, 0)向上是 (0, -1)。判斷時新方向的值不能和舊方向的值互為相反數。這里還要注意一個細節蛇頭更新位置前必須使用“未更新前”的方向判斷按鍵是否合法。如果先更新了蛇頭位置再根據按鍵改變方向就會出現極端情況下連按兩個鍵導致蛇反向穿體的問題。2.4 游戲循環與幀率控制Pygame 游戲的核心是一個無限循環。在這個循環里程序不斷完成四件事處理事件、更新游戲狀態、繪制畫面、控制幀率。貪吃蛇的移動不是每一幀都發生的而是每隔固定時間才移動一次。比如設置游戲運行速度是每秒 8 格那就意味著每 125 毫秒蛇移動一格幀率則可能穩定在 60 FPS畫面不會卡頓。通常用 pygame.time.Clock 對象控制幀率見后面的代碼示例。如果要實現“分數越高速度越快”只需要修改移動間隔時間速度從每秒 8 格逐步提升到每秒 14 格。2.5 碰撞檢測的三個方向碰撞檢測在貪吃蛇里分三種場景和墻壁的碰撞蛇頭坐標小于 0 或大于等于窗口格子數時游戲結束。和自身身體的碰撞蛇頭坐標出現在蛇身列表的非頭部位置時游戲結束。和食物的碰撞蛇頭坐標等于食物坐標時得分生成新食物蛇身增加一格。大多數情況下碰撞檢測放在移動動作完成之后。移動完成后再檢查蛇頭坐標這樣碰撞檢測的結果對應的是新一幀蛇的真實狀態符合玩家視覺感受。如果放在移動之前檢查會導致蛇已經貼墻但還沒判定為死亡體驗變差。3. 環境準備與前置條件3.1 安裝 Python在開始項目之前需要先確認電腦上安裝了 Python 環境。Pygame 目前支持 Python 3.8 以上的常見版本主流系統都兼容。這里不寫死具體版本因為 Python 更新比較快建議采用當前穩定版本即可。Windows 用戶可以直接從 Python 官網下載安裝包。安裝時一定要勾選“Add Python to PATH”這樣在命令行里輸入 python 時才能正常啟動。macOS 和 Linux 用戶優先使用系統自帶包管理器安裝比如 macOS 的 Homebrew、Ubuntu 的 apt。安裝完成后在終端或命令行輸入以下命令驗證python --version如果顯示類似 Python 3.10.12 這樣的信息說明安裝成功。如果提示找不到命令需要先檢查 PATH 配置或者嘗試用 python3 代替 python。3.2 安裝 PygamePygame 是一個第三方庫使用 pip 安裝命令如下pip install pygame如果電腦上同時存在 Python 2 和 Python 3可能需要使用 pip3。安裝完成后可以進入 Python 環境驗證python -c import pygame; print(pygame.version.ver)如果能輸出版本號說明 Pygame 已經可以正常導入。3.3 準備開發工具對于這個項目推薦使用 VS Code 作為編輯器。它有 Python 插件、智能提示、代碼運行按鈕對初學者比較友好。你也可以使用 PyCharm或者直接使用系統自帶的 IDLE。在工程項目里建議新建一個專門目錄比如 snake_game然后把代碼文件命名為 snake.py。文章后面所有代碼都默認放在這個文件里。4. 貪吃蛇游戲設計的核心流程寫代碼之前先把游戲流程想清楚這一步非常關鍵。很多初學者拿到題目就動手寫寫到一半發現邏輯混亂原因就是在開始前沒有把狀態機梳理明白。4.1 游戲整體狀態整個游戲可以分成四個階段INIT初始化窗口、pygame 模塊、蛇的位置、初始方向、食物位置、分數。RUNNING游戲主循環反復處理事件、移動蛇、繪制畫面。PAUSED按空格鍵暫停再按一次恢復。GAMEOVER蛇撞墻或撞到自己顯示結束文字、最終分數和重新開始提示。實現時不一定需要定義枚舉類可以用一個普通字符串變量來表示當前狀態比如 state RUNNING。初學者使用簡單的字符串狀態即可代碼更直觀。4.2 初始化步驟初始化需要完成以下任務實例化一個窗口設置寬度和高度。設置窗口標題。創建 Clock 對象。定義顏色常量。初始化蛇身長度和初始坐標。設置初始方向為向右。使用 random 模塊生成第一個食物的坐標。在 Pygame 中窗口坐標原點在左上角x 軸向右延伸y 軸向下延伸。這一點和數學坐標系不同初學者經常搞混。生成食物時要確保食物坐標和蛇身坐標不重疊。4.3 處理事件Pygame 的事件模型采用事件隊列。每幀循環開始時調用 pygame.event.get() 獲取當前幀的所有事件然后遍歷處理。常見事件包括QUIT用戶點擊窗口關閉按鈕。KEYDOWN用戶按下鍵盤。MOUSEBUTTONDOWN用戶點擊鼠標。處理鍵盤事件時需要注意方向鍵和 WASD 鍵都要支持這樣不同操作習慣的人都能方便控制。4.4 蛇的移動步驟蛇移動的偽代碼如下記錄舊蛇頭坐標 根據方向向量計算新蛇頭坐標 將新蛇頭坐標插入蛇身列表頭部 判斷是否吃到食物 如果吃到食物不刪除蛇尾 如果沒有吃到食物刪除蛇尾保持長度不變這個邏輯中有一個非常容易踩的坑判斷是否吃到食物必須發生在刪除蛇尾之前。因為如果先刪除了蛇尾蛇頭和食物重疊時刪除蛇尾不會影響判斷但給人一種邏輯混亂的感覺。更穩妥的方式是先計算新蛇頭再判斷是否吃到食物最后決定是否刪除蛇尾。4.5 繪制步驟繪制順序也需要注意。通常先填充背景色然后繪制食物再繪制蛇身最后繪制分數文字。如果先繪制蛇身再繪制食物食物如果恰好和蛇頭重合就會看到食物被蛇頭蓋住體驗不好。繪制蛇身時可以通過一個循環來畫每個坐標對應的矩形。為了美觀可以給每個格子留一點內邊距讓蛇身看起來像方塊之間有縫隙視覺效果更好。4.6 重新開始邏輯游戲結束后玩家按任意鍵或者按空格鍵都應該能重新開始。實現方式有兩種一種是重新運行游戲循環另一種是將游戲狀態重新初始化為 INIT 階段。推薦第二種只需要把初始化函數再調用一次所有變量恢復到初始值即可。5. 完整的 Python 貪吃蛇代碼實現下面提供完整代碼。代碼中會用到一些 Pygame 的基礎 API每個關鍵部分后面都有注釋說明。建議直接復制到 snake.py 文件中運行。import pygame import random import sys # 初始化 Pygame pygame.init() # 窗口和格子參數 CELL_SIZE 20 # 每個格子邊長20 像素 GRID_WIDTH 24 # 橫向格子數量 GRID_HEIGHT 24 # 縱向格子數量 WINDOW_WIDTH CELL_SIZE * GRID_WIDTH WINDOW_HEIGHT CELL_SIZE * GRID_HEIGHT # 幀率設置 FPS 60 # 顏色常量RGB BACKGROUND_COLOR (30, 30, 40) SNAKE_COLOR (0, 200, 100) FOOD_COLOR (220, 50, 50) TEXT_COLOR (255, 255, 255) OVERLAY_COLOR (0, 0, 0, 120) # 創建窗口 screen pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption(Python 貪吃蛇 - 練手項目) clock pygame.time.Clock() # 定義方向向量 DIRECTIONS { UP: (0, -1), DOWN: (0, 1), LEFT: (-1, 0), RIGHT: (1, 0), } def random_food(snake_body): 在空白位置隨機生成食物坐標 while True: x random.randint(0, GRID_WIDTH - 1) * CELL_SIZE y random.randint(0, GRID_HEIGHT - 1) * CELL_SIZE if (x, y) not in snake_body: return x, y def draw_text(content, size, color, x, y): 在窗口指定位置繪制文字 font pygame.font.SysFont(simhei, size) text_surface font.render(content, True, color) text_rect text_surface.get_rect(center(x, y)) screen.blit(text_surface, text_rect) def draw_snake(snake_body): 繪制蛇身 for index, (x, y) in enumerate(snake_body): # 蛇頭顏色稍作區分 color (100, 255, 150) if index 0 else SNAKE_COLOR rect pygame.Rect(x 1, y 1, CELL_SIZE - 2, CELL_SIZE - 2) pygame.draw.rect(screen, color, rect) def draw_food(food_pos): 繪制食物 x, y food_pos rect pygame.Rect(x 2, y 2, CELL_SIZE - 4, CELL_SIZE - 4) pygame.draw.rect(screen, FOOD_COLOR, rect) def show_game_over(snake_body, score): 顯示游戲結束畫面 overlay pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.SRCALPHA) overlay.fill(OVERLAY_COLOR) screen.blit(overlay, (0, 0)) draw_text(游戲結束, 48, TEXT_COLOR, WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 - 50) draw_text(f本局得分: {score}, 28, TEXT_COLOR, WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 10) draw_text(按 空格鍵 重新開始, 24, TEXT_COLOR, WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 60) def reset_game(): 初始化游戲狀態 start_x GRID_WIDTH // 2 * CELL_SIZE start_y GRID_HEIGHT // 2 * CELL_SIZE snake_body [ (start_x, start_y), (start_x - CELL_SIZE, start_y), (start_x - 2 * CELL_SIZE, start_y), ] direction DIRECTIONS[RIGHT] food_pos random_food(snake_body) score 0 move_interval 150 # 單位毫秒數值越小蛇移動越快 return snake_body, direction, food_pos, score, move_interval def main(): snake_body, direction, food_pos, score, move_interval reset_game() last_move_time pygame.time.get_ticks() running True paused False game_over False while running: # 1. 事件處理 for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: if game_over: if event.key pygame.K_SPACE: snake_body, direction, food_pos, score, move_interval reset_game() last_move_time pygame.time.get_ticks() game_over False paused False else: if event.key pygame.K_SPACE: paused not paused if not paused: if event.key in (pygame.K_UP, pygame.K_w): new_dir DIRECTIONS[UP] elif event.key in (pygame.K_DOWN, pygame.K_s): new_dir DIRECTIONS[DOWN] elif event.key in (pygame.K_LEFT, pygame.K_a): new_dir DIRECTIONS[LEFT] elif event.key in (pygame.K_RIGHT, pygame.K_d): new_dir DIRECTIONS[RIGHT] else: new_dir None if new_dir is not None: # 禁止 180 度反向 if (new_dir[0] direction[0], new_dir[1] direction[1]) ! (0, 0): direction new_dir if paused: # 2. 暫停狀態只繪制畫面不更新邏輯 screen.fill(BACKGROUND_COLOR) draw_snake(snake_body) draw_food(food_pos) draw_text(暫停中, 40, TEXT_COLOR, WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2) pygame.display.flip() clock.tick(FPS) continue if game_over: screen.fill(BACKGROUND_COLOR) draw_snake(snake_body) draw_food(food_pos) show_game_over(snake_body, score) pygame.display.flip() clock.tick(FPS) continue # 3. 控制蛇的移動頻率 current_time pygame.time.get_ticks() if current_time - last_move_time move_interval: last_move_time current_time head_x, head_y snake_body[0] dx, dy direction new_head (head_x dx * CELL_SIZE, head_y dy * CELL_SIZE) # 插入新蛇頭位置 snake_body.insert(0, new_head) # 判斷是否吃到食物 if new_head food_pos: score 1 food_pos random_food(snake_body) # 分數越高速度越快記錄移動間隔最短 80 毫秒 if move_interval 80: move_interval max(80, move_interval - 5) else: # 沒吃到食物則移除蛇尾保持長度不變 snake_body.pop() # 4. 碰撞檢測 head_x, head_y snake_body[0] if head_x 0 or head_x WINDOW_WIDTH or head_y 0 or head_y WINDOW_HEIGHT: game_over True if snake_body[0] in snake_body[1:]: game_over True # 5. 繪制畫面 screen.fill(BACKGROUND_COLOR) draw_food(food_pos) draw_snake(snake_body) draw_text(f得分: {score}, 24, TEXT_COLOR, 60, 25) pygame.display.flip() # 6. 控制幀率 clock.tick(FPS) pygame.quit() sys.exit() if __name__ __main__: main()這段代碼大約 220 行已經把貪吃蛇的核心功能完整實現了。下面拆解幾個關鍵點確保你不是只復制代碼而是真正理解它為什么這樣寫。首先是 reset_game 函數。它負責把蛇、方向、食物、分數和速度全部恢復到初始狀態。這樣做的好處是重新開始時只需要調用一次 reset_game所有狀態就能自動復位不會出現“上一次殘留的蛇身”或者“食物還在原處”的問題。其次是 move_interval 變量它控制蛇的移動間隔。初始值是 150 毫秒也就是每秒大約移動 6.67 格屬于比較舒適的速度。當分數增加時move_interval 每次減少 5 毫秒直到最小 80 毫秒。這樣實現了“分數越高速度越快”這個經典機制。方向反轉的判斷是一行關鍵代碼if (new_dir[0] direction[0], new_dir[1] direction[1]) ! (0, 0):如果新方向和當前方向相反它們的 x 和 y 分量相加會等于 (0, 0)比如向右 (1, 0) 和向左 (-1, 0)。這個技巧比逐一比較方向字符串更簡潔也避免了多個 if 分支。在碰撞檢測方面代碼先插入新蛇頭再判斷是否吃到食物最后才檢查碰撞。這個順序很重要如果不插入新蛇頭就無法用蛇頭坐標判斷碰撞如果先判斷碰撞再刪除蛇尾邏輯上也沒問題但代碼會多寫幾行不夠直觀。6. 運行結果與效果驗證6.1 運行命令把代碼保存為 snake.py 后在終端中運行python snake.py如果一切正常會彈出一個 480x480 的窗口深色背景一條綠色蛇出現在窗口中央蛇頭顏色稍亮蛇頭前方有一個紅色食物方塊。6.2 預期效果游戲默認方向是向右蛇會開始緩慢移動。使用方向鍵或 WASD 鍵可以控制蛇的移動方向。蛇頭碰到紅色食物時得分加 1食物會在新的隨機位置生成蛇身變長一格。連續吃到食物后蛇的移動速度會逐漸提升。按下空格鍵游戲暫停屏幕中央顯示“暫停中”文字再按一次恢復。蛇頭碰到窗口邊緣或蛇身時游戲進入結束狀態顯示“游戲結束”和本次得分。游戲結束后按空格鍵所有狀態重置開始新一輪游戲。6.3 如何判斷游戲運行成功窗口能正常打開沒有報錯閃退。蛇能持續移動方向控制符合預期。分數能正常遞增食物不會出現在蛇身體內部。撞墻和撞自身后游戲結束。重新開始后蛇身長度恢復到初始值分數歸零。如果你運行后遇到了問題不要急著懷疑代碼有問題先看下一部分的問題排查表。7. 常見問題與排查方法編寫和運行貪吃蛇的過程中最常見的問題集中在環境問題、代碼邏輯問題和游戲交互體驗問題三個層面。下面整理了一份排查表問題現象可能原因排查方式解決方案運行python snake.py后提示 ModuleNotFoundErrorPygame 未安裝或安裝到了其他 Python 環境執行pip list查看是否有 pygame安裝依賴pip install pygame檢查是否使用了一致的 python 命令窗口一閃而過程序直接退出代碼中pygame.quit()或sys.exit()位置有誤或某處拋出了未捕獲異常在終端運行程序查看報錯堆棧根據堆棧定位具體行常見問題是沒有while running循環或事件處理有誤蛇只能按一個方向移動按鍵沒有響應事件處理中方向鍵判斷寫錯或direction未被更新在按鍵分支里加 print 打印調試確認KEYDOWN事件分支縮進正確確認沒有在 game_over 狀態下誤處理按鍵蛇按反方向鍵后穿越身體沒有禁止 180 度反向檢查方向更新邏輯加上(new_dir[0] direction[0], new_dir[1] direction[1]) ! (0, 0)判斷蛇移動速度太快很難操作move_interval 初始值太小或 FPS 設置過高打印 move_interval 值觀察游戲感受把初始間隔調整到 150 毫秒或更高食物生成在蛇身上random_food 沒有排除蛇身坐標檢查生成函數確保while循環中(x, y) not in snake_body才返回坐標蛇身變長后畫面出現閃爍繪制順序不正確或未調用 display.flip檢查繪制代碼繪制完所有元素后再統一pygame.display.flip()窗口標題中文亂碼Pygame 默認字體不支持中文使用系統字體如 simhei調用pygame.font.SysFont指定 simhei 或中文字體程序占用 CPU 過高沒有調用 clock.tick() 控制幀率檢查主循環尾部在每幀結束添加clock.tick(FPS)按任意鍵都會重新開始重新開始邏輯綁定在了任意按鍵上檢查按鍵判斷只對pygame.K_SPACE觸發重新開始一個問題需要特別強調如果你發現運行代碼時終端顯示的是 Python 內置的 turtle 相關報錯或者 Pygame 版本過低請先更新 Pygame。老版本 Pygame 在 Windows 新系統上可能出現 API 兼容問題直接升級到當前穩定版本即可。8. 工程化改進與最佳實踐把貪吃蛇跑通只是第一步。作為一個練手項目建議你把代碼再重構一遍把很多東西做得更像一個真正的小型項目。8.1 用類來組織代碼現在的版本是函數式寫法已經足夠清晰。但如果你想進一步鍛煉面向對象思維可以嘗試把游戲封裝成一個 SnakeGame 類屬性包括 snake_body、direction、food_pos、score、move_interval、paused、game_over方法包括 handle_events、update、draw、reset、run。這樣主函數會非常簡潔而每個方法的職責也很明確。以后如果再寫一個俄羅斯方塊或掃雷你會驚訝地發現這個類的骨架幾乎可以復用。8.2 增加音效和視覺反饋Pygame 支持音樂和音效播放。可以準備一個極短的食物音效文件當吃到食物時播放這會讓游戲反饋更直觀。還可以在吃到食物時讓分數文字閃爍一下或者讓蛇頭顏色變化增強成就感。8.3 記錄最高分可以在游戲結束時把最高分保存到一個本地文本文件或者 JSON 文件中。下一次啟動游戲時讀取最高分并顯示在窗口左上角。這個功能會讓你接觸到文件讀寫、數據持久化的基本概念對后續學習非常有幫助。8.4 添加難度選擇和關卡系統在游戲開始前讓玩家選擇“簡單”“普通”“困難”三個難度分別對應不同的初始移動間隔。或者在吃到一定數量的食物后進入下一關背景顏色發生變化障礙物出現。這些擴展會讓游戲更有可玩性同時鍛煉你的需求拆解能力。8.5 命名規范和代碼可讀性建議所有常量大寫命名比如 CELL_SIZE、FPS所有變量名使用小寫字母和下劃線比如 snake_body、food_pos函數名使用動詞開頭比如 reset_game、draw_snake、handle_events。這些規范能讓代碼更容易閱讀和維護。8.6 注釋不是越多越好初學者容易陷入“每行寫注釋”的誤區。好的注釋應該解釋“為什么”而不是機械翻譯代碼。比如move_interval max(80, move_interval - 5)這一行注釋寫成“吃到一個食物后縮短 5 毫秒但低于 80 毫秒時不再提速”比“設置移動間隔”更有價值。核心邏輯和不易理解的算法點才值得注釋。8.7 使用版本管理工具即使這是一個小項目也建議你用 Git 管理代碼。初次提交一個可用版本然后在此基礎上添加新功能每個功能對應一次提交。這樣你能看到自己逐步迭代的過程也能學會撤銷修改、查看歷史版本等工程基本操作。8.8 測試在游戲里的實踐游戲項目可以做一些簡單的自動化測試。比如你寫了一個判斷蛇是否撞墻的函數就可以寫一組測試用例傳入不同的蛇頭坐標斷言函數返回值是否正確。Pygame 項目雖然不像后端項目那樣強調單元測試但把純邏輯和 UI 繪制分離后純邏輯部分是可以測試的。這個思路會讓你對“可測試代碼”有更直觀的理解。9. 總結與后續學習方向通過這個貪吃蛇項目你已經完整走過了一個 Python 小型項目的生命周期環境搭建、需求分析、邏輯設計、編碼實現、運行調試、擴展優化。這個過程中收獲的不僅僅是幾個 Pygame API 的用法更重要的是列表、字典、循環、函數、狀態管理、事件驅動這些基礎能力第一次真正組合起來變成了一個可以給別人展示的成果。如果你完成了基礎版下一步可以考慮這幾個方向深入 Pygame嘗試做俄羅斯方塊、飛機大戰、Flappy Bird理解動畫幀、精靈類、碰撞組、聲音模塊的用法。轉向圖形界面開發用 Tkinter 或 PySide6 做桌面工具復用已經掌握的“事件驅動 狀態管理”思維。轉向 Web 開發用 Flask 或 FastAPI 做 Web 項目很多思想是相通的比如請求-響應循環和游戲循環本質都是“等待輸入 - 處理 - 輸出反饋”。貪吃蛇看起來簡單但它是少數能把 Python 基礎語法和真實項目經驗連接起來的經典案例。建議你把文章中的代碼親手敲一遍不要直接復制運行敲完再去擴展一兩個功能。遇到報錯不要怕能看到報錯信息本身就說明你已經開始用程序員的方式思考了。