不間斷 Python 挑戰 Day 25 - 專題:貪食蛇(上)

更新於 發佈於 閱讀時間約 13 分鐘

接下來我們會運用之前所學過的各種觀念,包括Turtle 模組繼承等等,來製作這個大家都熟知的小遊戲-貪食蛇。完成品會長得像下圖這樣,包括可以設定難度(速度)、使用鍵盤的上、下、左、右鍵來移動貪食蛇的移動方向來吃到食物並加長蛇身、以及記分板等。

raw-image

遊戲架構

這個遊戲到目前為止算是一個稍微複雜的程式,將程式的邏輯拆解成一些較簡單的功能,再將這些功能組合起來,會比較利於遊戲設計的進行,以免不知道從何下手。這篇文章會以以下的流程來說明:

  1. 建立遊戲進行的畫布
  2. 建立遊戲難度設定視窗
  3. 畫出遊戲開始時的貪食蛇身體
  4. 畫出圍牆
  5. 隨機產生食物
  6. 移動蛇身
  7. 控制方向
  8. 檢查是否吃到食物
  9. 建立記分板
  10. 檢查是否撞到牆
  11. 檢查是否撞到蛇身
  12. 結束遊戲

建立遊戲進行的畫布

從Turtle模組載入Screen類別,建立一個600*600大小的畫布,並設定標題與背景顏色,這個畫布就是讓遊戲進行的區域。tracer()方法的目的是暫停畫布的更新,讓後續在畫布上所改變的內容可使用update()方法一次更新,以減少視覺上的延遲感。

# 主程式
from turtle import Screen

# 遊戲參數
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
SCREEN_COLOR = "black"
SCREEN_TITLE = "貪食蛇"

# 建立Screen物件
snake_screen = Screen()

# 遊戲畫面設定
snake_screen.setup(width=SCREEN_WIDTH, height=SCREEN_HEIGHT)
snake_screen.bgcolor(SCREEN_COLOR)
snake_screen.title(SCREEN_TITLE)
snake_screen.tracer(0)

# 畫面暫停
snake_screen.exitonclick()
raw-image

建立遊戲難度設定視窗

在這裡我們要讓使用者輸入遊戲的難度,所以必須另開一個對話視窗和使用者互動,並把輸入的內容做為參數傳入程式。在Screen類別中,可以使用textinput()方法來達成這個目標,用法如下:

turtle.textinput(title, prompt)
  • title – 對話視窗的標題
  • prompt – 對話視窗內顯示的文字

在這裡我們以1到9來代表遊戲的難度,並把輸入的內容存入變數,換算成畫面更新的時間間隔,接著就可以建立主程式的while迴圈,根據難度所換算出來的固定時間間隔更新畫面。

# 主程式
import time

# 遊戲難度
level = snake_screen.textinput(title="貪食蛇-遊戲難度", prompt="請選擇遊戲難度:1(簡單) ~ 9(困難)")
time_delay = (10 - float(level)) * 0.05

# 遊戲主程式
is_game_on = True
while is_game_on:
  # 遊戲畫面更新
  snake_screen.update()
  time.sleep(time_delay)
raw-image

畫出遊戲開始時的貪食蛇身體

為了簡潔起見,我們在主程式所在的目錄底下建立一個snake資料夾,把所有和這個專題相關的檔案都放在這個資料夾中,在主程式中導入。

蛇身的屬性和操作可以視為一個功能單位,因此首先在snake資料夾中新增一個snake.py檔案,在其中建立一個Snake類別,在初始化中定義三個Turtle物件做為蛇身,並適當設定三個物件的距離,讓蛇身可以連在一起。

# snake.py
from turtle import Turtle

START_POSITION = [(0, 0), (-20, 0), (-40, 0)]

class Snake:

  def __init__(self):
    self.snake_body = []
    self.create_snake()
    self.head = self.snake_body[0]
    self.move_angle_list = []

  def create_snake(self):
    for position in START_POSITION:
      self.add_snake_body(position)

  def add_snake_body(self, position):
    body_seg = Turtle(shape="square")
    body_seg.color("Green")
    body_seg.penup()
    body_seg.goto(position)
    self.snake_body.append(body_seg)

在Snake類別中特別把add_snake_body()獨立成一個方法,因為之後在延長蛇身時會重覆使用同一段程式碼。在主程式中載入Snake類別,並建立一個Snake物件。

# 主程式
from snake.snake import Snake

# 建立遊戲相關物件
snake = Snake()

執行後,可以在畫布上看到一段遊戲開始時的小蛇。

raw-image

畫出圍牆

圍牆同樣也具有獨立的屬性與功能,它在遊戲一開始建立後就不會再更動,目的是要讓蛇頭碰觸到圍牆後即觸發遊戲結束,因此圍牆的範圍需要考慮畫布的大小。我們同樣在snake資料夾中新增一個wall.py檔案,在其中建立Wall類別,讓它繼承自Turtle模組,以方便使用Turtle模組的所有功能,不須額外新增Turtle物件。Wall類別在初始化時從主程式傳入畫布的大小,以畫筆在畫布的四周圈出一個長方形的區域,這個區域即是貪食蛇可以在其中自由移動的範圍,注意上方預留了較大的空間,是顯示記分板文字的位置。

# wall.py
from turtle import Turtle

WALL_COLOR = "BlueViolet"
WALL_PEN_SIZE = 10

class Wall(Turtle):

  def __init__(self, width, height):
    super().__init__()
    self.hideturtle()
    self.pensize(WALL_PEN_SIZE)
    self.color(WALL_COLOR)
    self.speed("fastest")
    self.screen_width = width
    self.screen_height = height
    self.draw()

  def draw(self):
    self.penup()
    self.goto(-(self.screen_width / 2 - 25), self.screen_height / 2 - 45)
    self.pendown()
    self.forward(self.screen_width - 50)
    self.setheading(270)
    self.forward(self.screen_height - 70)
    self.setheading(180)
    self.forward(self.screen_width - 50)
    self.setheading(90)
    self.forward(self.screen_width - 70)

主程式中建立Wall類別的物件,即可在畫布四周畫出圍牆。

# 主程式
from snake.wall import Wall

# 建立遊戲相關物件
wall = Wall(SCREEN_WIDTH, SCREEN_HEIGHT)
raw-image

隨機產生食物

食物必須隨機在圍牆內產生,由前一小節圍牆的設定可知,畫筆在四個頂點的座標分別為:左上(-275, 255)、右上(275, 255)、左下(-275, -275)、右下(275, -275),畫筆的寬度為10,所以圍牆內緣的座標為:左上(-270, 250)、右上(270, 250)、左下(-270, -270)、右下(270, -270),每一個蛇身的物件距離為20,因此產生食物的範圍必須再內縮10(蛇身的一半),也就是x座標在[-260, 260]、y座標在[-260, 240]這個區間內產生食物。此外,為了之後配合貪食蛇每次行走的距離,也就是一個蛇身的長度20,讓蛇身和食物可以對齊,我們利用一個ALIGNMENT_FACTOR讓出現食物的座標皆為20的整數倍。

# food.py
import random
from turtle import Turtle

FOOD_SHAPE = "circle"
FOOD_COLOR = "gold"
ALIGNMENT_FACTOR = 20

class Food(Turtle):

  def __init__(self, width, height):
    super().__init__()
    self.shape(FOOD_SHAPE)
    self.penup()
    self.color(FOOD_COLOR)
    self.speed("fastest")
    self.screen_width = width
    self.screen_height = height
    self.random_food()

  def random_food(self):
    random_x = random.randint(-(self.screen_width / 2 - 40), (self.screen_width / 2 - 40))
    random_x = ALIGNMENT_FACTOR * round(random_x / ALIGNMENT_FACTOR)
    random_y = random.randint(-(self.screen_height / 2 - 40), (self.screen_width / 2 - 60))
    random_y = ALIGNMENT_FACTOR * round(random_y / ALIGNMENT_FACTOR)
    self.goto(random_x, random_y)

主程式中建立Food類別的物件,即可隨機出現第一個食物。

# 主程式
from snake.food import Food

# 建立遊戲相關物件
food = Food(SCREEN_WIDTH, SCREEN_HEIGHT)
raw-image

移動蛇身

先不考慮方向,若要讓蛇身往前移動,以目前的三節身體來說,就是讓第三節的身體往第二節身體移動、第二節身體往蛇頭移動、蛇頭往前移動一個單位,更多節的身體也可以以此類推,因此在Snake類別中新增move()方法實現以上邏輯。

# snake.py
MOVE_DISTANCE = 20

class Snake:

  def move(self):
    # 往前移動
    for body_seg in range(len(self.snake_body) - 1, 0, -1):
      new_x = self.snake_body[body_seg - 1].xcor()
      new_y = self.snake_body[body_seg - 1].ycor()
      self.snake_body[body_seg].goto(new_x, new_y)
    self.head.forward(MOVE_DISTANCE)

在主程式的while迴圈中呼叫Snake類別的move()方法,執行後可以看到三節的小蛇一路往前移動到超出畫布範圍。

# 主程式
# 遊戲主程式
is_game_on = True
while is_game_on:
  # 遊戲畫面更新
  snake_screen.update()
  time.sleep(time_delay)
  snake.move()
raw-image

接著還有六個步驟,留待下篇文章說明!


留言
avatar-img
留言分享你的想法!
avatar-img
Wei-Jie Weng的沙龍
48會員
36內容數
Wei-Jie Weng的沙龍的其他內容
2022/07/13
對於程式的初學者而言,理解程式的流程、迴圈的進行、或是變數的變化會需要一定程度將程式在腦中進行運算的能力,要一段時間熟悉與適應,尤其是當程式執行的結果不如預期時,往往是計算的過程和自己所想像的不同,這時又更難靠自己的能力找出錯誤。因此,這邊要介紹的這個工具可以將程式執行的過程逐行將變數的變化視覺化地
Thumbnail
2022/07/13
對於程式的初學者而言,理解程式的流程、迴圈的進行、或是變數的變化會需要一定程度將程式在腦中進行運算的能力,要一段時間熟悉與適應,尤其是當程式執行的結果不如預期時,往往是計算的過程和自己所想像的不同,這時又更難靠自己的能力找出錯誤。因此,這邊要介紹的這個工具可以將程式執行的過程逐行將變數的變化視覺化地
Thumbnail
2022/07/13
在上一節介紹了 JSON 資料的基本架構後,我們將改寫並擴充密碼產生器程式,讓它能夠藉由 JSON 的資料結構完成帳密搜尋的功能。
Thumbnail
2022/07/13
在上一節介紹了 JSON 資料的基本架構後,我們將改寫並擴充密碼產生器程式,讓它能夠藉由 JSON 的資料結構完成帳密搜尋的功能。
Thumbnail
2022/06/23
JSON的全名叫JavaScript Object Notation,是由Douglas Crockford所設計的一種資料格式,最初應用在JavaScript程式語言中,做為一種資料交換的格式,而後被廣泛運用在Web開發與NoSQL資料庫,現今已成為一種重要的資料格式。
Thumbnail
2022/06/23
JSON的全名叫JavaScript Object Notation,是由Douglas Crockford所設計的一種資料格式,最初應用在JavaScript程式語言中,做為一種資料交換的格式,而後被廣泛運用在Web開發與NoSQL資料庫,現今已成為一種重要的資料格式。
Thumbnail
看更多
你可能也想看
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
嗨嗨打給賀!挖系Serpens Spirit Studio ㄉ蛇靈~~ 蛇靈的最終目標是想製作出自己的遊戲,所以來和大家分享我的遊戲遊玩觀察&研究小筆記! 【攻擊提示&UI介面明顯】 音樂聲提醒敵人來了 固定顏色(遊戲中是黃色)提示玩家可以運用這裡通關 兩個角色的座標顏色固定,媽媽(木頭玩
Thumbnail
嗨嗨打給賀!挖系Serpens Spirit Studio ㄉ蛇靈~~ 蛇靈的最終目標是想製作出自己的遊戲,所以來和大家分享我的遊戲遊玩觀察&研究小筆記! 【攻擊提示&UI介面明顯】 音樂聲提醒敵人來了 固定顏色(遊戲中是黃色)提示玩家可以運用這裡通關 兩個角色的座標顏色固定,媽媽(木頭玩
Thumbnail
在完成一些简单的任务之后,我觉得ChatGPT可以帮我完成一些更为复杂,甚至于可能我并不是特别擅长的任务,而我想到了,也许,可以帮我写一个简单的游戏。 贪多嚼不烂的道理我还是明白的,于是,我给自己提前就设定了一个目标: 游戏可以运行实现简单的规则减少BUG的存在可以开始和结束 带着这四个最朴素
Thumbnail
在完成一些简单的任务之后,我觉得ChatGPT可以帮我完成一些更为复杂,甚至于可能我并不是特别擅长的任务,而我想到了,也许,可以帮我写一个简单的游戏。 贪多嚼不烂的道理我还是明白的,于是,我给自己提前就设定了一个目标: 游戏可以运行实现简单的规则减少BUG的存在可以开始和结束 带着这四个最朴素
Thumbnail
看來這應該是pygame的bug,而不是自己寫的程式有問題。為了進一步證實這個猜測,重寫了一個單純只畫出圓球的程式,除了畫出不同位置的圓球之外,沒有任何其他作用
Thumbnail
看來這應該是pygame的bug,而不是自己寫的程式有問題。為了進一步證實這個猜測,重寫了一個單純只畫出圓球的程式,除了畫出不同位置的圓球之外,沒有任何其他作用
Thumbnail
快速反應事件(Quick Time Events,簡稱 QTE)是電子遊戲中的一種遊戲機制,玩家必須在特定的時間框架內按下控制器或鍵盤上的按鈕,以便繼續遊戲或避免失敗。它們通常用於動作或冒險遊戲中,以增加場景的緊張感或興奮感。 本次開發,思考著2D橫向遊戲,解謎動作類,要如何更有趣,因此想到了QTE
Thumbnail
快速反應事件(Quick Time Events,簡稱 QTE)是電子遊戲中的一種遊戲機制,玩家必須在特定的時間框架內按下控制器或鍵盤上的按鈕,以便繼續遊戲或避免失敗。它們通常用於動作或冒險遊戲中,以增加場景的緊張感或興奮感。 本次開發,思考著2D橫向遊戲,解謎動作類,要如何更有趣,因此想到了QTE
Thumbnail
本作為像素風,最大特色是貪食蛇加彈幕遊戲,在Boss戰中,玩家須閃躲Boss的彈幕攻擊,並蒐集畫面上的資料,進行反擊,而主角的長度會因為攜帶的資料量而變長,增加閃躲的難度,玩法相當創新。
Thumbnail
本作為像素風,最大特色是貪食蛇加彈幕遊戲,在Boss戰中,玩家須閃躲Boss的彈幕攻擊,並蒐集畫面上的資料,進行反擊,而主角的長度會因為攜帶的資料量而變長,增加閃躲的難度,玩法相當創新。
Thumbnail
在那Nokia手機風靡全球的年代,因該有不少人玩過手機內建的貪吃蛇遊戲,記得當時年紀小在還是學生的那個年代就經常利用上電腦課的時候偷偷用我那隻好不容易打工購買的Nokia手機玩這款遊戲,玩到最後還利用電腦課的時間用BASIC寫出了一款簡易版的貪吃蛇遊戲。。。
Thumbnail
在那Nokia手機風靡全球的年代,因該有不少人玩過手機內建的貪吃蛇遊戲,記得當時年紀小在還是學生的那個年代就經常利用上電腦課的時候偷偷用我那隻好不容易打工購買的Nokia手機玩這款遊戲,玩到最後還利用電腦課的時間用BASIC寫出了一款簡易版的貪吃蛇遊戲。。。
Thumbnail
Blockade(封鎖線)這款你可能不知道的貪吃蛇類史祖,問世於1976年街機平台上,其可兩人同時對戰的設計與簡約卻又不簡單的遊戲模式,在當時還真讓不少人為之瘋狂。。。
Thumbnail
Blockade(封鎖線)這款你可能不知道的貪吃蛇類史祖,問世於1976年街機平台上,其可兩人同時對戰的設計與簡約卻又不簡單的遊戲模式,在當時還真讓不少人為之瘋狂。。。
Thumbnail
有些風景需要親自經歷後才能了解箇中滋味,這初衷,也可能讓您在20幾年後的今天,被現實打擊到身心疲憊時為了鼓舞自己大聲吶喊莫忘初衷後可以立即回憶起的經歷。 以上故事純屬虛構,如有雷同實屬巧合,以下開始本次教學。 依照慣例我們一樣先來看看完成後的的遊玩影片: A、提案企劃 一句話形容這個遊戲 遊戲類型
Thumbnail
有些風景需要親自經歷後才能了解箇中滋味,這初衷,也可能讓您在20幾年後的今天,被現實打擊到身心疲憊時為了鼓舞自己大聲吶喊莫忘初衷後可以立即回憶起的經歷。 以上故事純屬虛構,如有雷同實屬巧合,以下開始本次教學。 依照慣例我們一樣先來看看完成後的的遊玩影片: A、提案企劃 一句話形容這個遊戲 遊戲類型
Thumbnail
三張scratch積木程式圖示 大朋友小朋友都能參於 輕鬆完成經典貪食蛇遊戲!! 以學習為主要目標 完成了永不失敗的貪食蛇!! 內文提供SCRATCH連結可以回味一下貪食蛇遊戲
Thumbnail
三張scratch積木程式圖示 大朋友小朋友都能參於 輕鬆完成經典貪食蛇遊戲!! 以學習為主要目標 完成了永不失敗的貪食蛇!! 內文提供SCRATCH連結可以回味一下貪食蛇遊戲
Thumbnail
接下來我們會運用之前所學過的各種觀念,包括Turtle 模組、繼承等等,來製作這個大家都熟知的小遊戲-貪食蛇。完成品會長得像下圖這樣,包括可以設定難度(速度)、使用鍵盤的上、下、左、右鍵來移動貪食蛇的移動方向來吃到食物並加長蛇身、以及記分板等。
Thumbnail
接下來我們會運用之前所學過的各種觀念,包括Turtle 模組、繼承等等,來製作這個大家都熟知的小遊戲-貪食蛇。完成品會長得像下圖這樣,包括可以設定難度(速度)、使用鍵盤的上、下、左、右鍵來移動貪食蛇的移動方向來吃到食物並加長蛇身、以及記分板等。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News