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

閱讀時間約 14 分鐘
接續前一篇文章,說明貪食蛇遊戲架構的後半部份,來完成整個遊戲的程式設計。

遊戲架構(續)

控制方向

要透過鍵盤來控制貪食蛇移動的方向,需要使用到Turtle模組的按鍵事件方法,也就是透過鍵盤按鍵的按下或釋放來觸發某一段程式執行。在這裡,我們使用鍵盤的上、下、左、右鍵做為觸發事件的按鍵,當這些按鍵按下後,觸發某一段程式,其語法如下:
turtle.listen()
turtle.onkeypress(fun, key)
  • fun – 按鍵按下後執行的函數
  • key – 觸發事件的按鍵
當向上的按鍵按下後,如果當下貪食蛇的方向為向左或向右,在下一步即轉為向上移動,也就是說,如果當下貪食蛇的方向為向上或向下,該事件無效,因為蛇身已經同向,或是不能直接反轉,其它方向的事件以此類推。
首先我們在Snake類別的初始化函數內新增一個移動方向的串列。
# snake.py
self.move_angle_list = []
當上、下、左、右鍵按下後,立即在move_angle_list中新增一個元素,代表下一個要轉向的角度。
# snake.py
def move_up(self):
  self.move_angle_list.append(90)
def move_down(self):
  self.move_angle_list.append(270)
def turn_right(self):
  self.move_angle_list.append(0)
def turn_left(self):
  self.move_angle_list.append(180)
在move()方法中,新增一段程式檢查move_angle_list是否為空串列,若有元素存在,即執行轉向的動作,在下一步改變移動的方向。
# snake.py
def move(self):
  # 檢查是否要轉彎
  while self.move_angle_list:
    next_move_angle = self.move_angle_list.pop(0)
    if next_move_angle != self.head.heading() and \
      next_move_angle != (self.head.heading() + 180) % 360:
      self.head.setheading(next_move_angle)
      break;
    else:
      continue;
# 往前移動
  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)
在主程式中,則利用listen()及onkeypress()方法,接受上、下、左、右鍵按下的事件。
# 主程式
# 按鍵設定
snake_screen.listen()
snake_screen.onkeypress(snake.move_up, "Up")
snake_screen.onkeypress(snake.move_down, "Down")
snake_screen.onkeypress(snake.turn_left, "Left")
snake_screen.onkeypress(snake.turn_right, "Right")
如此一來便可用鍵盤來控制貪食蛇移動的方向,下一步就可以開始讓貪食蛇吃食物並延長蛇身。

檢查是否吃到食物

在遊戲中吃食物必定是用蛇頭去吃,我們可以在每一步去檢查蛇頭和食物的距離,當距離足夠小即表示有吃到食物,這是和蛇相關的行為,因此在Snake類別中新增一個方法來執行這個任務。
# snake.py
def is_collision_with_food(self, food):
  if self.head.distance(food) < 5:
    return True
  return False
在吃食物的同時也會增加身體的長度,同樣在Snake類別中新增一個方法,複製貪食蛇末端的元素到snake_body串列的最後方,當蛇身下一次移動時就會完成延長蛇身的動作。
# snake.py
def extend_snake(self):
  self.add_snake_body(self.snake_body[-1].position())
最後,在主程式的while迴圈中,每一步移動之後即檢查是否吃到食物,若有,則呼叫food.random_food()隨機產生下一個食物,並執行snake.extend_snake()延長蛇身。
# 主程式
# 是否吃到食物
if snake.is_collision_with_food(food):
  food.random_food()
  snake.extend_snake()
執行結果:

建立記分板

上一篇文章畫出圍牆的時候,我們有在畫布的上方預留比較大的空間,做為顯示記分板文字的空間,在貪食蛇吃到食物的同時更新分數,例如一次加一分。做為一個相對獨立的功能,我們在snake資料夾內另開一個scoreboard.py檔案,建立Scoreboard類別,繼承自Turtle模組。在初始化函數中移動至適當的位置,設定初始分數為0,並用write()方法寫到畫布上。
# scoreboard.py
from turtle import Turtle
SCORE_COLOR = "white"
SCORE_POSITION = (0, 265)
class Scoreboard(Turtle):
  def __init__(self):
    super().__init__()
    self.score = 0
    self.hideturtle()
    self.penup()
    self.color(SCORE_COLOR)
    self.speed("fastest")
    self.goto(SCORE_POSITION)
    self.write(f"score: {self.score}", False, align="center", font=("Arial", 20, "normal"))
  def get_score(self):
    self.score += 1
    self.clear()
    self.write(f"score: {self.score}", False, align="center", font=("Arial", 20, "normal"))
主程式中建立Scoreboard類別的物件,並在while迴圈中當偵測到蛇頭吃到食物時呼叫scoreboard.get_score()方法更新分數。
# 主程式
# 建立遊戲相關物件
scoreboard = Scoreboard()
# 遊戲主程式
is_game_on = True
while is_game_on:
  # 遊戲畫面更新
  snake_screen.update()
  time.sleep(time_delay)
  snake.move()
  # 是否吃到食物
  if snake.is_collision_with_food(food):
    food.random_food()
    snake.extend_snake()
    scoreboard.get_score()
執行結果:

檢查是否撞到牆

程式來到了最後的步驟,要決定遊戲何時該結束,可以歸納出兩個條件:
  1. 蛇頭撞到牆。
  2. 蛇頭撞到身體。
先處理撞到牆的部份,判斷撞到牆的條件是蛇頭超出了圍牆的範圍,前篇文章已知圍牆內緣的座標為:左上(-270, 250)、右上(270, 250)、左下(-270, -270)、右下(270, -270),我們便以此為條件在Snake類別新增is_collision_with_wall()方法來判斷。
# snake.py
def is_collision_with_wall(self, width, height):
  if self.head.xcor() > (width / 2 - 30) or \
    self.head.xcor() < -(width / 2 - 30) or \
    self.head.ycor() > (height / 2 - 50) or \
    self.head.ycor() < -(height / 2 - 30):
    return True
  return False
主程式的while迴圈中,每一次更新位置後呼叫snake.is_collision_with_wall()判斷是否撞牆。
# 主程式
# 是否撞牆
if snake.is_collision_with_wall(width=SCREEN_WIDTH, height=SCREEN_HEIGHT):
  is_game_on = False
執行結果:

檢查是否撞到蛇身

要判斷蛇頭是否撞到蛇身,我們將蛇頭和蛇身每一節的距離一一比對,當距離小於20,也就是每前進一次的距離,就表示撞到自己了,當然設定更小也是沒有問題的。
# snake.py
def is_collision_with_body(self):
  for body_seg in self.snake_body[1:]:
    if self.head.distance(body_seg) < 5:
      return True
  return False
在主程式的while迴圈中,同樣在每一次更新位置後呼叫snake.is_collision_with_body(),來判斷是否相撞。
# 主程式
# 是否撞身體
if snake.is_collision_with_body():
  is_game_on = False
執行結果:

結束遊戲

為了讓遊戲結束時有明確的標示,讓遊戲看起來更完整,我們讓前面兩個條件成立時進一步在畫布上顯示出"Game Over"字樣以提示遊戲結束,因為性質和記分板類似,我們也在Scoreboard類別中新增此功能。
# scoreboard.py
def game_over(self):
  self.color(GAMEOVER_COLOR)
  self.goto(GAMEOVER_POSITION)
  self.write("Game Over", False, align="center", font=("Arial", 40, "normal"))
最後,在判斷是否相撞的條件內呼叫scoreboard.game_over()方法,整個遊戲便大功告成。
# 主程式
# 遊戲主程式
is_game_on = True
while is_game_on:
  # 遊戲畫面更新
  snake_screen.update()
  time.sleep(time_delay)
  snake.move()
  # 是否吃到食物
  if snake.is_collision_with_food(food):
    food.random_food()
    snake.extend_snake()
    scoreboard.get_score()
  # 是否撞牆
  if snake.is_collision_with_wall(width=SCREEN_WIDTH, height=SCREEN_HEIGHT):
    is_game_on = False
    scoreboard.game_over()
  # 是否撞身體
  if snake.is_collision_with_body():
    is_game_on = False
    scoreboard.game_over()
"Game Over"顯示畫面:

程式範例

為什麼會看到廣告
avatar-img
47會員
36內容數
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Wei-Jie Weng的沙龍 的其他內容
接下來我們會運用之前所學過的各種觀念,包括Turtle 模組、繼承等等,來製作這個大家都熟知的小遊戲-貪食蛇。完成品會長得像下圖這樣,包括可以設定難度(速度)、使用鍵盤的上、下、左、右鍵來移動貪食蛇的移動方向來吃到食物並加長蛇身、以及記分板等。
Turtle Graphic的前身是一種設計給小朋友學習的簡易繪圖程式,最初來自於Wally Feurzeig、Seymour Papert和Cynthia Solomon於1967年所創造的Logo編程語言,它是Python內建的繪圖函式庫,我們可以根據函式庫裡面的指令,操控一隻或多隻「小烏龜」在
「繼承」顧名思義就是有一個或多個類別延續了某個類別的特性,就如同在人類社會裡,兒女接收了父母的財產、承襲了上代的技能、延續了前一輩的事業。在Python的語言裡,能夠繼承的特性為類別的屬性與方法,繼承的類別稱為子類別(child class / subclass)或衍伸類別(derived clas
在類別一節中,我們可以用Student類別的實體來存取類別中的name變數、score字典、以及其中的所有方法,這些可以被類別以外的程式碼所直接存取的屬性稱為公有屬性(public attribute)、可以被類別以外的程式碼所直接呼叫的方法稱為公有方法(public method)。
大型程式的開發通常不會由一個人獨力完成,而是將程式依功能拆分成不同的函數或是類別,由不同人協力開發成各個模組,最後再將這些模組導入主程式,或是直接套用現有的模組。模組依來源可以分成三大類: Python內建的模組 外部模組,需從第三方的軟體儲存庫(如PyPI)並安裝使用 程式開發者自行開發的模組
全球公衛教授和公共教育家漢斯.羅斯林(Hans Rosling)在其著名的暢銷書「真確」中,提出了13個關於全球貧富、人口、出生/死亡率、教育、公衛等的「留意認知落差測驗」,回答的人不乏受過良好教育的專業人士,結果顯示,受測者在前12題平均只答對2題,遠不如隨機亂選的黑猩猩(如果黑猩猩會回答問題的話
接下來我們會運用之前所學過的各種觀念,包括Turtle 模組、繼承等等,來製作這個大家都熟知的小遊戲-貪食蛇。完成品會長得像下圖這樣,包括可以設定難度(速度)、使用鍵盤的上、下、左、右鍵來移動貪食蛇的移動方向來吃到食物並加長蛇身、以及記分板等。
Turtle Graphic的前身是一種設計給小朋友學習的簡易繪圖程式,最初來自於Wally Feurzeig、Seymour Papert和Cynthia Solomon於1967年所創造的Logo編程語言,它是Python內建的繪圖函式庫,我們可以根據函式庫裡面的指令,操控一隻或多隻「小烏龜」在
「繼承」顧名思義就是有一個或多個類別延續了某個類別的特性,就如同在人類社會裡,兒女接收了父母的財產、承襲了上代的技能、延續了前一輩的事業。在Python的語言裡,能夠繼承的特性為類別的屬性與方法,繼承的類別稱為子類別(child class / subclass)或衍伸類別(derived clas
在類別一節中,我們可以用Student類別的實體來存取類別中的name變數、score字典、以及其中的所有方法,這些可以被類別以外的程式碼所直接存取的屬性稱為公有屬性(public attribute)、可以被類別以外的程式碼所直接呼叫的方法稱為公有方法(public method)。
大型程式的開發通常不會由一個人獨力完成,而是將程式依功能拆分成不同的函數或是類別,由不同人協力開發成各個模組,最後再將這些模組導入主程式,或是直接套用現有的模組。模組依來源可以分成三大類: Python內建的模組 外部模組,需從第三方的軟體儲存庫(如PyPI)並安裝使用 程式開發者自行開發的模組
全球公衛教授和公共教育家漢斯.羅斯林(Hans Rosling)在其著名的暢銷書「真確」中,提出了13個關於全球貧富、人口、出生/死亡率、教育、公衛等的「留意認知落差測驗」,回答的人不乏受過良好教育的專業人士,結果顯示,受測者在前12題平均只答對2題,遠不如隨機亂選的黑猩猩(如果黑猩猩會回答問題的話
你可能也想看
Google News 追蹤
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
Thumbnail
2024.07.20 感覺自從這週蟲蟲出去浪了四天回來之後,情勒仔只要看到蟲蟲就各種監視+緊盯不放。 今天蟲哥說情勒仔想要出來走走(因為行動不便現在都待在蟲哥房間裡),就把他帶到蟲蟲房間,還叫蟲蟲開冷氣給他吹。 然後情勒仔進來之後就找好了位置趴下來但一直沒睡著,而且那個趴的位置跟以
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
Thumbnail
嗨嗨打給賀!挖系Serpens Spirit Studio ㄉ蛇靈~~ 蛇靈的最終目標是想製作出自己的遊戲,所以來和大家分享我的遊戲遊玩觀察&研究小筆記! 【攻擊提示&UI介面明顯】 音樂聲提醒敵人來了 固定顏色(遊戲中是黃色)提示玩家可以運用這裡通關 兩個角色的座標顏色固定,媽媽(木頭玩
Thumbnail
慶典運作模式: 前面站裹蜜蟲,後面啪咚猴,旁邊祭典會場 就可以享受一回合任意拿卡,打兩下100+ 優開對局:噴火龍,猛雷草,純古代小人 劣開對局:洛奇亞,多龍,接力棒感激放大,鋼系抗草的... 不太確定:放逐,沙奈朵 對局流程: 前面站蟲,後面鋪猴子跟下一隻蟲 釣竿通常吊一組蟲+
Thumbnail
本文介紹了Python中的流程控制,包括if, elif, else語句,三元運算子,for和while迴圈,以及控制迴圈語句如break、continue和pass。透過範例程式碼,說明了如何使用這些語句和結構進行條件判斷,迴圈遍歷和控制程式流程。
Thumbnail
小烏龜小烏龜 來吧!動吃動吃,凍吃凍吃... 你最近是不是吃太多烤雞了? 左左、右右!你們佔據胸膛上 就算寄生也要愛惜宿主拾緣哥 吃好吃滿是享福,發福可不好 上次要你們牽腳腳,當手牽手 這次我沒想到什麼好的勞動法 且先訓練龜派氣功或龜息大法 拾緣哥!放下你的麵,別吃了 好好管
Thumbnail
固定行為模式 許多動物經常會被特定條件觸發特定行為。 例如有一種肉食螢火蟲a會捕食另一種螢火蟲b。 a會在b的繁殖季發出b種雌蟲的光,然後b就會自動飛過去送頭。 另一個例子則是火雞,火雞馬麻會對一種幼火雞特殊的嘰嘰叫產生反應並做出育兒行為。 富有實驗精神的科學家就用火雞天敵——臭鼬的形象做了個會發出
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
Thumbnail
2024.07.20 感覺自從這週蟲蟲出去浪了四天回來之後,情勒仔只要看到蟲蟲就各種監視+緊盯不放。 今天蟲哥說情勒仔想要出來走走(因為行動不便現在都待在蟲哥房間裡),就把他帶到蟲蟲房間,還叫蟲蟲開冷氣給他吹。 然後情勒仔進來之後就找好了位置趴下來但一直沒睡著,而且那個趴的位置跟以
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
Thumbnail
嗨嗨打給賀!挖系Serpens Spirit Studio ㄉ蛇靈~~ 蛇靈的最終目標是想製作出自己的遊戲,所以來和大家分享我的遊戲遊玩觀察&研究小筆記! 【攻擊提示&UI介面明顯】 音樂聲提醒敵人來了 固定顏色(遊戲中是黃色)提示玩家可以運用這裡通關 兩個角色的座標顏色固定,媽媽(木頭玩
Thumbnail
慶典運作模式: 前面站裹蜜蟲,後面啪咚猴,旁邊祭典會場 就可以享受一回合任意拿卡,打兩下100+ 優開對局:噴火龍,猛雷草,純古代小人 劣開對局:洛奇亞,多龍,接力棒感激放大,鋼系抗草的... 不太確定:放逐,沙奈朵 對局流程: 前面站蟲,後面鋪猴子跟下一隻蟲 釣竿通常吊一組蟲+
Thumbnail
本文介紹了Python中的流程控制,包括if, elif, else語句,三元運算子,for和while迴圈,以及控制迴圈語句如break、continue和pass。透過範例程式碼,說明了如何使用這些語句和結構進行條件判斷,迴圈遍歷和控制程式流程。
Thumbnail
小烏龜小烏龜 來吧!動吃動吃,凍吃凍吃... 你最近是不是吃太多烤雞了? 左左、右右!你們佔據胸膛上 就算寄生也要愛惜宿主拾緣哥 吃好吃滿是享福,發福可不好 上次要你們牽腳腳,當手牽手 這次我沒想到什麼好的勞動法 且先訓練龜派氣功或龜息大法 拾緣哥!放下你的麵,別吃了 好好管
Thumbnail
固定行為模式 許多動物經常會被特定條件觸發特定行為。 例如有一種肉食螢火蟲a會捕食另一種螢火蟲b。 a會在b的繁殖季發出b種雌蟲的光,然後b就會自動飛過去送頭。 另一個例子則是火雞,火雞馬麻會對一種幼火雞特殊的嘰嘰叫產生反應並做出育兒行為。 富有實驗精神的科學家就用火雞天敵——臭鼬的形象做了個會發出