貪吃蛇作為一款極其經典且廣受歡迎的小游戲,是早期 Windows 電腦和功能手機(特別是諾基亞手機)流行度極高的小游戲,是當時功能手機時代最具代表性的游戲之一。游戲的基本規則和目標十分簡單,但卻極具吸引力,讓人欲罷不能。本博文我們用 Python 編寫屬於自己的貪吃蛇游戲,一起來體驗一下編程的樂趣與... ...
貪吃蛇作為一款極其經典且廣受歡迎的小游戲,是早期 Windows 電腦和功能手機(特別是諾基亞手機)流行度極高的小游戲,是當時功能手機時代最具代表性的游戲之一。游戲的基本規則和目標十分簡單,但卻極具吸引力,讓人欲罷不能。本博文我們用 Python 編寫屬於自己的貪吃蛇游戲,一起來體驗一下編程的樂趣與成就……
本文 Python 貪吃蛇游戲源代碼:https://gitee.com/obullxl/PythonCS/tree/master/CS-CY2405
上篇Python消消樂小游戲和源代碼:https://mp.weixin.qq.com/s/QExc_gT-AKlDHWEN1Ycnlw
貪吃蛇游戲分析
控制蛇的移動:通過上下左右鍵,控制一條蛇在游戲區域中移動,最初蛇很短,通常由 1 個方塊組成。
吃到食物增長:游戲區域中會隨機出現食物(例如一個方塊),當蛇頭觸碰到食物時,代表蛇吃到了食物,蛇身體會增長一節,同時得 1 分。
避免越界或碰撞:游戲中需要避免蛇頭撞到游戲區域的邊界,或者蛇頭碰到自己的身體。
策略性移動:隨著游戲的進行蛇身增長,需要巧妙地操控蛇的路徑,既要吃到食物,又要避免越界碰撞,這變得越來越具挑戰性和趣味性。
游戲分數和結束:游戲過程中,需要記錄當前得分(即:蛇吃到食物的數量),游戲結束,展示總得分和重新開始游戲或者退出。
準備:安裝 pygame 工具包
貪吃蛇游戲依賴pygame
這個強大的 Python 游戲工具包:
pip install pygame
代碼:設置基礎參數
基礎參數包括屏幕大小、中文字體、背景顏色、字體顏色、蛇的顏色、食物的顏色、蛇的大小和蛇游動速度等。中文simsun.ttf字體文件在源代碼目錄,請一起下載到本地:
import random
import pygame
# 初始化
pygame.init()
# 設置視窗和初始化
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
SCREEN = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# 設置視窗標題
pygame.display.set_caption('老牛同學:貪吃蛇')
# 游戲屏幕字體
SCORE_FONT = pygame.font.Font('./fonts/simsun.ttf', 25)
RESULT_FONT = pygame.font.Font('./fonts/simsun.ttf', 25)
# 顏色設置
BLACK = (0, 0, 0) # 黑色(屏幕的顏色)
WHITE = (255, 255, 255) # 白色(分數的顏色)
GREEN = (0, 255, 0) # 綠色(蛇的顏色)
RED = (255, 0, 0) # 紅色(食物的顏色,游戲結束字體的顏色)
# 蛇區塊大小(正方形)和游動速度
SNAKE_BLOCK = 10
SNAKE_SPEED = 8
代碼:繪製游戲進行中的得分
游戲進行中,得分預設在視窗左上角展示:
def draw_score(score):
"""繪製當前分數"""
score_text = SCORE_FONT.render("總分數:" + str(score), True, WHITE)
#SCREEN.blit(score_text, [20, 20]) # 左上角
也可以設置為頂部居中展示,如下代碼:
def draw_score(score):
"""繪製當前分數"""
score_text = SCORE_FONT.render("總分數:" + str(score), True, WHITE)
score_rect = score_text.get_rect(center=(SCREEN_WIDTH // 2, 20))
SCREEN.blit(score_text, score_rect)
代碼:繪製游戲進行中蛇的身體
游戲進行中,蛇的身體其實就是一些方塊的位置,蛇的數據結構為一個 list 列表,列表的元素是 x 和 y 坐標 list 列表,即[[x1,y1],[x2,y2],[x3,y3]...]
數據存儲形式,蛇尾是第 1 個元素,蛇頭是在最後 1 個元素。
def draw_snake(snake_list):
"""繪製蛇的身體,由於都是方塊,所以繪製過程無需區分蛇頭和蛇身等"""
for x in snake_list:
pygame.draw.rect(SCREEN, GREEN, [x[0], x[1], SNAKE_BLOCK, SNAKE_BLOCK])
問題:蛇尾在第 1 個元素,而蛇頭在最後 1 個元素,為什麼要這麼設計?
答案:從後面代碼可以看出,蛇在游動的過程中,蛇新的坐標是用的append
到列表,然後刪除列表的第 1 個元素。當然完全可以通過insert
的方式反過來設計。
代碼:計算食物的隨機坐標
我們使用random
函數隨機計算食物的坐標,同時需要註意,避免食物越界:
def food_position():
"""隨機計算食物坐標"""
x_food = round(random.randrange(0, SCREEN_WIDTH - SNAKE_BLOCK, SNAKE_BLOCK))
y_food = round(random.randrange(0, SCREEN_HEIGHT - SNAKE_BLOCK, SNAKE_BLOCK))
return x_food, y_food
代碼:繪製游戲結束分數和提示
我們在屏幕正中央分別展示 3 行提示文本:游戲結束、總得分和繼續游戲提示。文本正中央展示和總得分類似:
def draw_result(snake_length):
"""繪製游戲結果"""
# 在屏幕中央顯示文本
game_over_text = RESULT_FONT.render('游戲結束', True, RED)
game_over_rect = game_over_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 50))
SCREEN.blit(game_over_text, game_over_rect)
# 顯示最終得分文本
final_score_text = RESULT_FONT.render(f'總得分: {snake_length - 1}', True, RED)
final_score_rect = final_score_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
SCREEN.blit(final_score_text, final_score_rect)
# 顯示重新開始游戲的提示文本
restart_text = RESULT_FONT.render('按`Q`退出游戲,按`C`重新開始游戲', True, RED)
restart_rect = restart_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50))
SCREEN.blit(restart_text, restart_rect)
代碼:游戲主迴圈設置
主迴圈游戲把以上功能函數縫合起來,同時包括控制代碼:
game_close
參數控制單局游戲是否結束,game_over
參數控制整個游戲是否結束- 上下左右按鍵:上下按鍵控制蛇Y軸(上減少、下增加),左右按鍵控制蛇X軸(左減少、右增加)
- 蛇觸牆檢測:蛇頭坐標x1,y1是否在整個屏幕之內
- 蛇觸蛇身檢測:蛇頭坐標是否在蛇身體的任意坐標相同
- 蛇吃到食物檢測:蛇頭坐標是否和食物坐標相同,如果相同則重新設置食物位置,同時蛇身長加 1 個方塊
def game_loop():
"""游戲主迴圈函數"""
game_over = False # 退出游戲
game_close = False # 單次游戲結束
# 初始化蛇的坐標和坐標增量
x1 = SCREEN_WIDTH / 2
y1 = SCREEN_HEIGHT / 2
x1_change = 0
y1_change = 0
# 蛇的身體列表,初始長度為1
snake_list = []
snake_length = 1
# 隨機生成食物的位置
x_food, y_food = food_position()
while not game_over:
# 如果游戲結束但未選擇退出或重玩,則進入此迴圈
while game_close:
# 清空屏幕,準備下一輪繪製
SCREEN.fill(BLACK)
draw_result(snake_length)
pygame.display.update() # 刷新屏幕
# 等待按鍵
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
game_over = True # 退出游戲
game_close = False
if event.key == pygame.K_c:
game_loop() # 重新開始游戲
# 處理鍵盤事件,改變蛇的移動方向
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_over = True # 退出游戲
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
# 左:X坐標減少1個區塊,Y坐標不變
x1_change = -1
y1_change = 0
elif event.key == pygame.K_RIGHT:
# 右:X坐標增加1個區塊,Y坐標不變
x1_change = 1
y1_change = 0
elif event.key == pygame.K_UP:
# 上:X坐標不變,Y坐標減少1個區塊
x1_change = 0
y1_change = -1
elif event.key == pygame.K_DOWN:
# 下:X坐標不變,Y坐標增加1個區塊
x1_change = 0
y1_change = 1
# 退出游戲
if game_over:
break
# 檢測蛇是否觸牆
if x1 >= SCREEN_WIDTH or x1 < 0 or y1 >= SCREEN_HEIGHT or y1 < 0:
game_close = True
# 更新蛇的位置
x1 += x1_change * SNAKE_BLOCK
y1 += y1_change * SNAKE_BLOCK
# 清空屏幕,準備下一輪繪製
SCREEN.fill(BLACK)
# 畫食物
pygame.draw.rect(SCREEN, RED, [x_food, y_food, SNAKE_BLOCK, SNAKE_BLOCK])
# 新的蛇頭位置,同時刪除最後蛇尾區塊,以保持蛇的總長度不變
snake_head = [x1, y1]
snake_list.append(snake_head)
if len(snake_list) > snake_length:
del snake_list[0] # 刪除蛇尾
# 檢查蛇頭是否碰到蛇的身體
for x in snake_list[:-1]:
if x == snake_head:
game_close = True
# 繪製蛇
draw_snake(snake_list)
# 繪製得分
draw_score(snake_length - 1)
# 刷新屏幕
pygame.display.update()
# 檢查蛇頭是否碰到食物,若碰到則增加長度並重新生成食物
if x1 == x_food and y1 == y_food:
x_food, y_food = food_position()
snake_length += 1
# 控制游戲幀率
clock = pygame.time.Clock()
clock.tick(SNAKE_SPEED)
# 游戲結束時清理pygame環境
pygame.quit()
quit()
最後:啟動游戲主迴圈
啟動游戲,進入主迴圈,通過上下左右按鍵控制蛇身游動:
# 開始游戲
if __name__ == '__main__':
game_loop()
最後,我們游戲界面大概像這樣:
單局游戲結束的界面:
禪定: 我們可以繼續進一步優化這個程式:
- 每次吃到食物、單局游戲結束增加音效
- 增加難度:長按上下左右按鍵,加速蛇游動的速度
- 記住每輪游戲分數,進行分數排名(本地記錄,或者聯網)
關註本公眾號,我們一起探尋編程的樂趣!
我的本博客原地址:https://ntopic.cn/p/2024052301
本文作者:奔跑的蝸牛,轉載請註明原文鏈接:https://ntopic.cn