pygame寫貪吃蛇

来源:https://www.cnblogs.com/ansver/archive/2018/02/23/8438830.html
-Advertisement-
Play Games

python小白嘗試寫游戲.. 學了點pygame不知道那什麼練手好,先拿貪吃蛇開刀吧. 一個游戲可以粗略的分為兩個部分: 數據(變數) 處理數據(函數,方法) 設計變數 首先預想下,畫面的那些部分需要存儲在變數里 整個畫面上只會有矩形,而且這些矩形整整齊齊,大小相等,原本一個矩形需要四個變數表示位 ...


python小白嘗試寫游戲..

學了點pygame不知道那什麼練手好,先拿貪吃蛇開刀吧.

一個游戲可以粗略的分為兩個部分:

  • 數據(變數)
  • 處理數據(函數,方法)

設計變數

首先預想下,畫面的那些部分需要存儲在變數里

整個畫面上只會有矩形,而且這些矩形整整齊齊,大小相等,原本一個矩形需要四個變數表示位置,這裡,只需要兩個變數(行數,列數)就能表示方塊的位置

蛇頭,食物可以用二元元組表示,蛇身的數量不確定,只能用包含數個元組的列表表示

另外設定視窗大小800x600,每個方塊都是50x50

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("貪吃蛇")

food = (4, 5)
body = [(1, 1),(1,2)]
head = (1, 3)

BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

變數設定好了,游戲已經完成了一半( ̄▽ ̄)~*

下一步

變數到畫面

pygame.draw.rect()是根據矩形四元數組繪製圖像的,那就寫個函數"對接"下我的二元坐標

這裡就成數學的問題了...

def new_draw_rect(zb, color,screen):
    pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48))

鑒於50x50時相鄰方塊們會"粘"在一起,方塊向里收一下成48x48        ↑

繪製圖形,

...
while
1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() screen.fill(WHITE) new_draw_rect(food, RED, screen) for i in body: new_draw_rect(i, BLUE, screen) new_draw_rect(head, GREEN, screen) pygame.display.update()

 由靜到動

 兩個問題:

1.什麼時候動

2.怎麼動

問題1,什麼時候動,這裡有兩個思路,

  • 間隔固定時間(1秒),動一次
  • 按一次鍵動一次,無操作一定時間(1秒)後,重覆最後一次操作

看起來第一種方案 簡單 不錯

首先,先用pygame的clock類限制幀率(100幀),以方便計時

再者,加入新變數times,times每次加1,超過一百就"動一動"

加入新變數direction,表示蛇頭朝向,銜接鍵盤操作和"怎麼動"

 PS:程式結束之前,很難知道要用多少變數

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("貪吃蛇")

fclock = pygame.time.Clock()


food = (4, 5)
body = [(1, 1)]
head = (1, 2)
times = 0
direction = 'right'

BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255

def new_draw_rect(zb, color,screen):
    pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48))
    pass


while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                direction = "up"
            elif event.key == pygame.K_LEFT:
                direction = "left"
            elif event.key == pygame.K_DOWN:
                direction = "down"
            elif event.key == pygame.K_RIGHT:
                direction = "right"
    if times >= 100:
        pass#動一動
        times = 0
    else:
        times += 1

    screen.fill(WHITE)
    new_draw_rect(food, RED, screen)
    for i in body:
        new_draw_rect(i, BLUE, screen)
    new_draw_rect(head, GREEN, screen)

    fclock.tick(100)
    pygame.display.update()

 

 蛇頭的運動規律 : 向臨近的格子移動,上下左右具體那個格子由鍵盤確定

那就寫個新函數去生成蛇頭的新位置

def get_front(head,direction):
    x, y = head
    if direction == "up":
        return x-1, y
    elif direction == "left":
        return x, y-1
    elif direction == "down":
        return x+1, y
    elif direction == "right":
        return x, y+1

然後

head = get_front(head,direction)

但是蛇蛇一頭扎牆裡怎麼辦.......

你的好友【front : 臨時記下蛇頭前方的位置】已上線

你的好友【alive : 記錄存活信息】已上線

PS:front可以不是全局變數

def ask_alive(front,body):
    x, y = front
    if x < 0 or x > 12 or y < 0 or y >16 :
        return False
    if front in body:
        return False
    return True

然後這樣用

front = get_front(head,direction)
alive = ask_alive(front,body)
if alive:
  head = front

人話 :先看看蛇頭前面是否有危險,有危險就死了 , 不動啦

另外alive得加到前面的if裡面當限制條件,死了就不能亂動啦~~~~~

 

蛇身動的規律 : 近頭端跟頭走,尾端也跟著走--向程式靠攏-->>用過的head加入body,同時刪去body最老的成員

這裡可以看出body必須有序,可變.python裡面就用列表了

如果使用list的append方法,head加在body的末尾,那麼body[0]就會是"最老的成員"就得使用pop(0)刪去

PS. body.append(head)得寫在head=front前面,在head更新前加進body

    if times >= 100 and alive:
        front = get_front(head,direction)
        alive = ask_alive(front,body)
        if alive:
            body.append(head)
            head = front
            body.pop(0)
        times = 0
    else:
        times += 1

 食物的運動規律:被吃掉後,隨機位置再出現 --向程式靠攏-->> 當head== food為真時 food隨機選擇一個蛇之外的地方出現

def new_food(head,body):
    while 1:
        x = random.randint(1, 12)
        y = random.randint(1, 16)
        if (x, y) != head and (x, y) not in body:
            return x, y

 

這裡存在一個隱式BUG,要是蛇充滿了每一個角落,那這就是死迴圈 ...........然後整個程式卡在這裡....

版本1 : 我選擇沒看見,不會有什麼人能吃到"全屏"的(´⊙ω⊙`)    --來自開發者的懶惰

版本2 【我的選擇】: 修了這個BUG

def new_food(head,body):
    i = 0
    while i < 100:
        x = random.randint(1, 12)
        y = random.randint(1, 16)
        if (x, y) != head and (x, y) not in body:
            return (x, y), True
        i += 1
    else:
        return (0, 0), False

food, alive = new_food()

 

 100次機會 否則就死( ̄へ ̄)     --來自開發者的惡意

另外,蛇吃了食物就要長一格====>>蛇頭前進一格,蛇尾不動,蛇就因此長了一格====>>當head = food 為真時body.pop(0)不用執行

        if alive:
            body.append(head)
            head = front
            if food == head:
                food = new_food(head, body)
            else:
                body.pop(0)

 

完結撒花

細節優化

1,

什麼?游戲結束了?

黑個屏,提醒下

你的好友【back_color】已上線

back_color一開始等於WHITE , alive為假時變為BLOCK

標題欄變成"游戲結束"

    if times >= 100 and alive:
        front = get_front(head, direction)
        alive = ask_alive(front, body)
        if alive:
            body.append(head)
            head = front
            if food == head:
                food, alive = new_food(head, body)
            else:
                body.pop(0)
        else:
            back_color = BLOCK
            pygame.display.set_caption("游戲結束")
        times = 0
    else:
        times += 1

 

 

 

2,

要是一開始按了一下left......

恭喜你獲得技能【撞脖子自殺】

要麼那就

你的好友【old_direction】已上線

old_direction = "right"

def direction_yes_no(direction,old_direction):
    d = {"up": "down", "down": "up", "left": "right", "right": "left"}
    if d[direction] == old_direction:
        return old_direction
    return direction

 

PS : 字典代替if-elif結構省心省時

然後 

  if times >= 100 and alive:
        direction = direction_yes_no(direction, old_direction)
        old_direction = direction
        front = get_front(head, direction)
        alive = ask_alive(front, body)

 

人話 : 添加一個變數記錄上一次有效的輸入,兩個方向關係不正確時,以老變數為準--向人靠攏-->>向右跑時,不准向左!!!

Q : direction也可以在按鍵事件處理時限制呀,例如

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                direction = "up"
                   """改為"""
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                if direction != "down"
                    direction = "up"

 

A:在發現這個方法的BUG之前,我也是這樣想的

還是上面的例子

直接按left行不通,right--XX-->left

先按down,迅速再按left ,,right --pass-->> down --pass-->> left

恭喜你獲得技能【繞過開發者防護,高水平撞脖子自殺】

3,

Q : 我要暫停!!!

A : 好好玩游戲,不要動不動就暫停

你的好友【pause】已上線

邏輯值,P鍵控制

pause = False

            elif event.key == pygame.K_p:
                pause = not pause    

if times >= 100 and alive and (not pause):
    ....

 

 

下麵我發一下全部的代碼,代碼包含上面的優化

↓↓↓↓

完結撒花

import pygame
import sys
import random

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("貪吃蛇")

fclock = pygame.time.Clock()


food = (4, 5)
body = [(1, 1)]
head = (1, 2)
times
= 0 direction = "right" old_direction = "right" alive = True pause = False BLOCK = 0, 0, 0 GREEN = 0, 255, 0 RED = 255, 0, 0 BLUE = 0, 0, 255 WHITE = 255, 255, 255 back_color = WHITE def new_draw_rect(zb, color,screen): pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48)) def get_front(head, direction): x, y = head if direction == "up": return x-1, y elif direction == "left": return x, y-1 elif direction == "down": return x+1, y elif direction == "right": return x, y+1 def ask_alive(front, body): x, y = front if x < 0 or x > 12 or y < 0 or y >16 : return False if front in body: return False return True def new_food(head, body): i = 0 while i < 100: x = random.randint(1, 12) y = random.randint(1, 16) if (x, y) != head and (x, y) not in body: return (x, y), True i += 1 else: return (0, 0), False def direction_yes_no(direction, old_direction): d = {"up": "down", "down": "up", "left": "right", "right": "left"} if d[direction] == old_direction: return old_direction return direction

#food = new_food(head,body)
while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: direction = "up" elif event.key == pygame.K_LEFT: direction = "left" elif event.key == pygame.K_DOWN: direction = "down" elif event.key == pygame.K_RIGHT: direction = "right" elif event.key == pygame.K_p: pause = not pause if times >= 100 and alive and (not pause): direction = direction_yes_no(direction, old_direction) old_direction = direction front = get_front(head, direction) alive = ask_alive(front, body) if alive: body.append(head) head = front if food == head: food, alive = new_food(head, body) else: body.pop(0) else: back_color = BLOCK pygame.display.set_caption("游戲結束") times = 0 else: times += 1 screen.fill(back_color) new_draw_rect(food, RED, screen) for i in body: new_draw_rect(i, BLUE, screen) new_draw_rect(head, GREEN, screen) fclock.tick(100) pygame.display.update()

 

 

↑↑↑↑

↑↑↑↑

↑↑↑↑

4,

Q : 游戲結束或者暫停後因為times >= 100 and alive and (not pause)始終為假

times += 1 一直運行,,,,會不會不太妥當

A : 又不會溢出,,,,,,,,取消暫停時蛇能迅速跑起來要是time==100那問題就大了,,幸虧當初留了些餘地寫成 >=  

(~ ̄▽ ̄)~ 

5,

Q : 蛇跑的太慢我想加速

A : 上面的times>=100的100隨便改一下就行

0->100動一動   變成    0->80 動一動

或者動前面的數

0->100動一動   變成    20->100動一動

或者將它設置成變數讓它隨蛇的長度變化而變化

6,

打字太累,,

direction那裡可以使用0,1,2,3

代替 up left down right

鍵盤事件和get_front()得用同一套詞

7,

Q : 第一個food的位置是固定的,不能"動"嗎?

A : 因為我是先定義變數,再定義函數

food = new_food(head,body)

就得寫在定義函數後面,我不忍心讓它一個變數孤單,,,,怕你看不到(寫了但註釋掉了)

動手術

前面說過

問題1,什麼時候動,這裡有兩個思路,

  • 間隔固定時間(1秒),動一次
  • 按一次鍵動一次,無操作一定時間(1秒)後,重覆最後一次操作

看起來第一種方案 簡單 不錯

當初我以為第二種方案很難,,,

寫了這個博客後實力大增ᕦ( ᴼ ڡ ᴼ )ᕤ

現在我不怕了

第一個方法,

處理按鍵事件後times = 100 ,例

    if event.key == pygame.K_UP:
        direction = "up"
        times = 100
    elif event.key == pygame.K_LEFT:
        direction = "left"
        times = 100  

 

while迴圈當輪就能動一動

隱藏操作 : 在0.01s內按下兩個鍵,第一個按的不會"生效"

pygame : 我得跑完事件列表的每個元素

這是假的第二方案!!!

第二個方法,

把"動一動"的全部代碼,打包成函數或者直接寫在按鍵處理的後面

就是上面times = 100的位置 例

    if event.key == pygame.K_UP:
        direction = "up"
        '動一動'
    elif event.key == pygame.K_LEFT:
        direction = "left"
        '動一動'

 

記住關鍵的一點times = 0也算"動一動"的內容,第二方案里,按鍵後計時需要清零!!!

    if times >= 100 and alive and (not pause):
        direction = direction_yes_no(direction, old_direction)
        old_direction = direction
        front = get_front(head, direction)
        alive = ask_alive(front, body)
        if alive:
            body.append(head)
            head = front
            if food == head:
                food, alive = new_food(head, body)
            else:
                body.pop(0)
        else:
            back_color = BLOCK
            pygame.display.set_caption("游戲結束")
        times = 0
    else:
        times += 1

 

為了防止"詐屍"

"動一動"前還得加上if alive and (not pause):

times沒必要加

我就懶得管了(╥╯^╰╥)這篇文章寫了太長時間,現在已經看不懂當初的代碼了

Q : 不知道你是不是記得我,優化2就是我問的.....是這樣的,我對我的方法還不死心...."動手術"改第二方案後能用嗎?

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                direction = "up"
                   """改為"""
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                if direction != "down"
                    direction = "up"

 

A : 對於第一方法實現的第二方案還是會有小毛病

while迴圈當輪就能動一動

隱藏操作 : 在0.01s內按下兩個鍵,第一個按的不會"生效"

pygame : 我得跑完事件列表的每個元素

在0.01s內,按下兩個鍵,就可以照舊觸發【秘技: 撞脖子自殺】

第二方法實現的應該沒問題.......

 完結撒花✿✿ヽ(°▽°)ノ✿

 

Q : 我想在加寬屏幕在右側顯示一下時間分數之類的信息,你還有什麼"交待"嗎?

A : 在pygame顯示字體比較有難度....我只能祝你程式不出BUG...另外點(800,0)到點(800,600)別忘了畫到線,提醒玩家"邊界"還是存在的,讓玩家摔鍵盤動怒就不好了

 

Q : 我就是對你的顏色搭配有意見,顏色有點扎眼....

A : 我又不是美工,,,,,,,,,,,,顏色搭配的問題,應該,,,,,,,,,應該可以原諒,,,,,,,,,,

 

Q : 游戲太沒挑戰性,加點障礙物唄~~

A : walls 會在ask_alive()和new_food()用到,加個if的問題,,,,另外繪製屏幕那裡多加個for,,,,,

我就懶得管了(╥╯^╰╥)這篇文章寫了太長時間,現在已經看不懂當初的代碼了

完結撒花✿✿ヽ(°▽°)ノ✿

這次真沒了..✿✿ヽ(°▽°)ノ✿

如有疏漏,歡迎補充


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

-Advertisement-
Play Games
更多相關文章
  • ⚠️組件的作用域是孤立的,vue解決組件傳值問題是通過props ⚠️子傳父的時候需要vm.$emit觸發實例上的事件,頁面需要定一個方法去取值 ⚠️一定要註意命名方式和書寫,例如mylChart和myl-chart dataRadio和data-radio :objline和@data-radio ...
  • 之前的文章大量的內容在和大家探討分散式存儲,接下來的章節進入了分散式計算領域。坦白說,個人之前專業的重心側重於存儲,對許多計算的內容理解可能不是和確切,如果文章中的理解有所不妥,願虛心賜教。本篇將和大家聊一聊分散式計算的一個子集: 批處理 。 批處理系統通常也叫離線系統 ,需要大量的輸入數據,運行一 ...
  • Related Links:Zuul https://github.com/Netflix/zuulCAT https://github.com/dianping/catApollo https://github.com/ctripcorp/apolloKairosDB https://github... ...
  • 前面我們曾有篇文章中提到過關於用tensorflow訓練手寫28 28像素點的數字的識別,在那篇文章中我們把手寫數字圖像直接碾壓成了一個784列的數據進行識別,但實際上,這個圖像是28 28長寬結構的,我們這次使用CNN捲積神經網路來進行識別。 捲積神經網路我的理解是部分模仿了人眼的功能。 我們在看 ...
  • 在Java5.0之前,協調對共用對象的訪問可以使用的機制只有synchronized和volatile。我們知道synchronized關鍵字實現了內置鎖,而volatile關鍵字保證了多線程的記憶體可見性。在大多數情況下,這些機制都能很好地完成工作,但卻無法實現一些更高級的功能,例如,無法中斷一個正 ...
  • 目錄: Python之路第一篇——認識Python ...
  • 本來覺得沒什麼可寫的,因為網上這玩意一搜一大把,不過爬蟲畢竟是python的一個大亮點,不說說感覺對不起這玩意基礎點來說,python2寫爬蟲重點需要兩個模塊,urllib和urllib2,其實還有re先介紹下模塊的一些常用功能urllib.urlopen('http://xxx.xxx.xxx') ...
  • 此文章會講述簡單標簽處理器,因為經典自定義標簽處理器沒有簡單標簽處理器方便使用,故在此不進行描述。 參考:慕課網的《JSP自定義標簽》視頻; 《Servlet、JSP和Spring MVC初學指南》的第六章; IBM的《利用 JSP 2 提供的 SimpleTagSupport 開發自定義標簽》; ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...