Python游戲編程:一步步用Python打造經典貪吃蛇小游戲

来源:https://www.cnblogs.com/obullxl/p/18212076/NTopic2024052301
-Advertisement-
Play Games

貪吃蛇作為一款極其經典且廣受歡迎的小游戲,是早期 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)

代碼:游戲主迴圈設置

主迴圈游戲把以上功能函數縫合起來,同時包括控制代碼:

  1. game_close參數控制單局游戲是否結束,game_over參數控制整個游戲是否結束
  2. 上下左右按鍵:上下按鍵控制蛇Y軸(上減少、下增加),左右按鍵控制蛇X軸(左減少、右增加)
  3. 蛇觸牆檢測:蛇頭坐標x1,y1是否在整個屏幕之內
  4. 蛇觸蛇身檢測:蛇頭坐標是否在蛇身體的任意坐標相同
  5. 蛇吃到食物檢測:蛇頭坐標是否和食物坐標相同,如果相同則重新設置食物位置,同時蛇身長加 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()

最後,我們游戲界面大概像這樣:

游戲進行中界面

單局游戲結束的界面:

游戲結束界面

禪定: 我們可以繼續進一步優化這個程式:

  1. 每次吃到食物、單局游戲結束增加音效
  2. 增加難度:長按上下左右按鍵,加速蛇游動的速度
  3. 記住每輪游戲分數,進行分數排名(本地記錄,或者聯網)

關註本公眾號,我們一起探尋編程的樂趣!


我的本博客原地址:https://ntopic.cn/p/2024052301


微信公眾號:老牛同學

本文作者:奔跑的蝸牛,轉載請註明原文鏈接:https://ntopic.cn


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 背景 根據orangepi zero2用戶手冊說明,linux5.13內核不能使用 modprobe fbtft_device 驅動spi lcd 查看linux內核源碼提交記錄,發現在v5.4-rc3中刪除了fbtft_device.c文件 commit如下 staging/fbtft: Remo ...
  • 一、卸載mariadb的rpm包 1、首先,你需要找出已安裝的MariaDB包的具體名稱。可以使用以下命令列出所有已安裝的MariaDB包: rpm -qa | grep mariadb 2、刪除命令(安裝mysql不一定需要卸載)yum -y remove +【上圖的文件名】或者rpm -e -- ...
  • 前言 應用中的信息傳遞是為了實現各種功能和交互。信息傳遞可以幫助用戶和應用之間進行有效的溝通和交流。通過信息傳遞,應用可以向用戶傳遞重要的消息、通知和提示,以提供及時的反饋和指導。同時,用戶也可以通過信息傳遞嚮應用發送指令、請求和反饋,以實現個性化的需求和操作。 信息傳遞還可以幫助應用之間實現數 ...
  • Symbol 引用 iconfont icon圖標庫 Symbol 引用 這是一種全新的使用方式,應該說這才是未來的主流,也是平臺目前推薦的用法。相關介紹可以參考這篇文章 這種用法其實是做了一個 SVG 的集合,與另外兩種相比具有如下特點: 支持多色圖標了,不再受單色限制。 通過一些技巧,支持像字體 ...
  • XML中的字元串數據類型表示字元序列,包括換行、回車和製表符。處理器不修改值。`normalizedString`去除這些特殊字元,`token`則進一步移除前導和尾隨空格及多餘空格。字元串類型可使用枚舉、長度等限制。`date`和`dateTime`數據類型表示日期和時間,`duration`表示... ...
  • 一、是什麼 HMR全稱 Hot Module Replacement,可以理解為模塊熱替換,指在應用程式運行過程中,替換、添加、刪除模塊,而無需重新刷新整個應用 例如,我們在應用運行過程中修改了某個模塊,通過自動刷新會導致整個應用的整體刷新,那頁面中的狀態信息都會丟失 如果使用的是 HMR,就可以實 ...
  • theme: nico 寫在前面 主頁有更多其他篇章的方法,歡迎訪問查看。 本篇我們介紹radash中對象相關方法的使用和源碼解析。 assign:遞歸合併兩個對象 使用說明 功能說明:類似於 JavaScript 的 Object.assign 方法,用於將 override 對象的屬性和值複製到 ...
  • title: Vue 3 組件基礎與模板語法詳解 date: 2024/5/24 16:31:13 updated: 2024/5/24 16:31:13 categories: 前端開發 tags: Vue3特性 CompositionAPI Teleport Suspense Vue3安裝 組件 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...