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
  • 一個自定義WPF窗體的解決方案,借鑒了呂毅老師的WPF製作高性能的透明背景的異形視窗一文,併在此基礎上增加了滑鼠穿透的功能。可以使得透明窗體的滑鼠事件穿透到下層,在下層窗體中響應。 ...
  • 在C#中使用RabbitMQ做個簡單的發送郵件小項目 前言 好久沒有做項目了,這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作,之前在我的個人博客裡面回覆評論和友鏈申請是會通過發送郵件來通知對方的,不過當時只是簡單的進行了非同步操作。 那麼這次來使用RabbitMQ去統一發送郵件,我的想法是通過 ...
  • 當你使用Edge等瀏覽器或系統軟體播放媒體時,Windows控制中心就會出現相應的媒體信息以及控制播放的功能,如圖。 SMTC (SystemMediaTransportControls) 是一個Windows App SDK (舊為UWP) 中提供的一個API,用於與系統媒體交互。接入SMTC的好 ...
  • 最近在微軟商店,官方上架了新款Win11風格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,這款應用引入了前沿的Fluent Design UI設計,為用戶帶來全新的視覺體驗。 ...
  • 1.簡單使用實例 1.1 添加log4net.dll的引用。 在NuGet程式包中搜索log4net並添加,此次我所用版本為2.0.17。如下圖: 1.2 添加配置文件 右鍵項目,添加新建項,搜索選擇應用程式配置文件,命名為log4net.config,步驟如下圖: 1.2.1 log4net.co ...
  • 之前也分享過 Swashbuckle.AspNetCore 的使用,不過版本比較老了,本次演示用的示例版本為 .net core 8.0,從安裝使用開始,到根據命名空間分組顯示,十分的有用 ...
  • 在 Visual Studio 中,至少可以創建三種不同類型的類庫: 類庫(.NET Framework) 類庫(.NET 標準) 類庫 (.NET Core) 雖然第一種是我們多年來一直在使用的,但一直感到困惑的一個主要問題是何時使用 .NET Standard 和 .NET Core 類庫類型。 ...
  • WPF的按鈕提供了Template模板,可以通過修改Template模板中的內容對按鈕的樣式進行自定義。結合資源字典,可以將自定義資源在xaml視窗、自定義控制項或者整個App當中調用 ...
  • 實現了一個支持長短按得按鈕組件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。還可以和自定義外觀相結合,實現自定義的按鈕外形。 ...
  • 一、WTM是什麼 WalkingTec.Mvvm框架(簡稱WTM)最早開發與2013年,基於Asp.net MVC3 和 最早的Entity Framework, 當初主要是為瞭解決公司內部開發效率低,代碼風格不統一的問題。2017年9月,將代碼移植到了.Net Core上,併進行了深度優化和重構, ...