1. 程式人生 > >面向 python 小白的貪吃蛇遊戲

面向 python 小白的貪吃蛇遊戲

面向 python 小白的貪吃蛇遊戲

 

引言

作為python 小白,總是覺得自己要做好百分之二百的準備,才能開始寫程式。以至於常常整天在那看各種語法教程,學了幾個月還是隻會print('hello world')。

面向 python 小白的貪吃蛇遊戲

 

這樣做效率太低,正確的做法,是到身邊找問題,然後程式設計實現。比如說,我學了高等數學,我是不是應該考慮下如何去用程式設計實現求導或者積分操作,如果想不出怎麼辦,是不是應該 baidu 一下,別人是如何實現數值積分或是符號積分的。我們每天買東西都要用到加減甚至乘除,那麼我是否能編寫個簡單的計算器,如果命令列太醜的話,我是否能夠快速地學一學 pyqt5或是其他 gui來實現精緻些的應用程式。凡事用程式設計思維考慮一下,對於從程式設計小白進階為程式設計入門是大有裨益的。

小時候,我們或多或少會沉迷於一款經久不衰的遊戲------貪吃蛇。或許我們玩過各式各樣的貪吃蛇遊戲,卻沒有自己動手編寫屬於自己的貪吃蛇遊戲。今天就讓我們走進貪吃蛇的世界,用 python 實現簡易版的貪吃蛇遊戲。

遊戲簡介

首先是遊戲效果圖:

面向 python 小白的貪吃蛇遊戲

 

貪吃蛇[00-00-08--00-00-28].gif

使用者通過操控貪吃蛇,去吃到儘可能多的食物。其中貪吃蛇不能碰到牆壁,也不能咬到自身。

本教程藉助 pygame實現遊戲介面,所以下面稍稍介紹一下 pygame的安裝,用法就在下面連同函式一起講了:

安裝:

pip install -U pygame

接下來讓我們介紹下實現貪吃蛇的關鍵邏輯:

貪吃蛇的身體是由list構成的,list中每一個元組代表貪吃蛇在棋盤上的座標,我們只需在這些位置畫上圖案,就能製作出一條圓滾滾的貪吃蛇來。但是如果想讓貪吃蛇活蹦亂跳,我們就要寫一個move函式。

那麼貪吃蛇怎麼移動呢?

如果貪吃蛇沒吃到食物,那麼我們就刪除list中最後一個座標,再在蛇頭部分插入新的位置。如何確定新的位置呢,我們就要設定貪吃蛇移動的方向(x,y),將原蛇頭位置的座標在移動方向上進行加減操作。這樣貪吃蛇就實現了向前移動的目標。如果貪吃蛇恰好吃到了食物,唯一的不同就是不需要刪除貪吃蛇尾部的元素。其中需要注意的是,貪吃蛇不能朝著當前移動方向的反方向移動。體現在程式碼中,就是當前方向與改變方向的乘積不能為負值。

那麼如何知道貪吃蛇吃到了食物呢?

如果貪吃蛇蛇頭的座標與食物的座標重合的話,貪吃蛇就吃到了食物。如果貪吃蛇吃到了食物,就在棋盤上隨機更新食物。如果隨機生成的食物的座標,恰好與貪吃蛇的位置重合的話,就繼續隨機產生座標,直到確保與貪吃蛇的座標不同的時候。

那麼如何知道遊戲失敗了呢?

如果貪吃蛇蛇頭的座標與邊框的座標重合的話,蛇卒。如果貪吃蛇各個部分的座標有重合的話,就說明貪吃蛇咬到了自己,遊戲結束。

接下來是各個部分的具體程式碼實現:

下圖為主要需要的幾個函式:

面向 python 小白的貪吃蛇遊戲

 

程式碼.PNG

首先來看貪吃蛇模組:首先__init__初始化貪吃蛇的位置,初始方向豎直向上。toward函式用於改變貪吃蛇的方向,(x,y)分別表示蛇頭在水平和豎直方向的朝向。朝左x=-1,朝右x=1,朝上y=-1,朝下y=1。move函式,使用標誌enlarge來判斷蛇是否吃到了食物,並進行相應的操作。eat_food函式判斷蛇是否吃到食物,吃到的話,分數加 100,並返回True。toward函式,用於改變蛇頭的方向,但如果改變方向與當前方向相反,就什麼操作都不執行。draw函式用於畫出貪吃蛇的模樣,蛇頭是略大一點的紅心,蛇身是小一點的黃心。

我們怎麼畫出這條蛇呢?這就要藉助函式pygame.draw.circle,這個函式的主要引數有screen:就是你要在其中畫出貪吃蛇的遊戲介面,color:圖案的顏色(RGB), position:圖案在螢幕上的位置, radius:的半徑,width:內部填色的大小,如果為零,圖案就是空心圓;如果與半徑大小相同,圖案就是實心圓。

下面是貪吃蛇的程式碼部分,大家可以結合註釋閱讀:

# 貪吃蛇
class Snack(object):
 def __init__(self):
 # self.item = [(3, 25), (2, 25), (1, 25), (1,24), (1,23),
 # (1,22), (1,21), (1,20), (1,19), (1,18), (1,17), (1,16)]
 # x 水平方向 y 豎直方向
 # 初始方向豎直向上
 self.item = [(3, 25), (2, 25), (1, 25), (1, 24), ]
 self.x = 0
 self.y = -1
 def move(self, enlarge):
 # enlarge 標記貪吃蛇有沒有吃到食物
 if not enlarge:
 # 吃到食物刪除尾部元素
 self.item.pop()
 # 新蛇頭的座標為舊蛇頭座標加上移動方向的位移
 head = (self.item[0][0] + self.x, self.item[0][1] + self.y)
 # 將新的蛇頭座標插入在 list 最前面
 self.item.insert(0, head)
 def eat_food(self, food):
 global score
 # snack_x,snack_y 蛇頭座標
 # food_x, food_y 食物座標
 snack_x, snack_y = self.item[0]
 food_x, food_y = food.item
 # 比較蛇頭座標與食物座標
 if (food_x == snack_x) and (food_y == snack_y):
 score += 100
 return 1
 else:
 return 0
 def toward(self, x, y):
 # 改變蛇頭朝向
 if self.x * x >= 0 and self.y * y >= 0:
 self.x = x
 self.y = y
 def get_head(self):
 # 獲取蛇頭座標
 return self.item[0]
 def draw(self, screen):
 # 畫出貪吃蛇
 # 蛇頭為半徑為 15 的紅色實心圓
 radius = 15
 width = 15
 # i:1---34 j:1---25
 color = 255, 0, 0
 # position 為圖形的座標
 position = 10 + 20 * self.item[0][0], 10 + 20 * self.item[0][1]
 pygame.draw.circle(screen, color, position, radius, width)
 # 蛇身為半徑為 10 的黃色實心圓
 radius = 10
 width = 10
 color = 255, 255, 0
 for i, j in self.item[1:]:
 position = 10 + 20 * i, 10 + 20 * j
 pygame.draw.circle(screen, color, position, radius, width)

其次是食物模組:

np.random.randint用於產生邊界之內的座標,如果與貪吃蛇的座標重合,那麼就繼續生成新的隨機座標。

# 食物
class Food(object):
 def __init__(self):
 self.item = (4, 5)
 # 畫出食物
 def _draw(self, screen, i, j):
 color = 255, 0, 255
 radius = 10
 width = 10
 # i:1---34 j:1---25
 position = 10 + 20 * i, 10 + 20 * j
 # 畫出半徑為 10 的粉色實心圓
 pygame.draw.circle(screen, color, position, radius, width)
 # 隨機產生食物
 def update(self, screen, enlarge, snack):
 if enlarge:
 self.item = np.random.randint(1, BOARDWIDTH - 2), np.random.randint(1, BOARDHEIGHT - 2)
 while self.item in snack.item:
 self.item = np.random.randint(1, BOARDWIDTH - 2), np.random.randint(1, BOARDHEIGHT - 2)
 self._draw(screen, self.item[0], self.item[1])

然後是init_board函式:

board_width、board_height分別為遊戲介面的寬度和高度,根據計算得出邊框佔據的位置,然後打印出正方形來。pygame.draw.rect和pygame.draw.circle用法類似,區別就是rect四個引數分別為screen:螢幕,color:顏色,pos:橫座標 x,縱座標 y,矩形的長,矩形的寬。這裡我設定矩形長寬都為 20 。width和circle中width用法相同,都是填充大小的意思。

# 初始介面
def init_board(screen):
 board_width = BOARDWIDTH
 board_height = BOARDHEIGHT
 color = 10, 255, 255
 width = 0
 # width:x, height:y
 # 左右邊框佔用了 X: 0 35*20
 for i in range(board_width):
 pos = i * 20, 0, 20, 20
 pygame.draw.rect(screen, color, pos, width)
 pos = i * 20, (board_height - 1) * 20, 20, 20
 pygame.draw.rect(screen, color, pos, width)
 # 上下邊框佔用了 Y: 0 26*20
 for i in range(board_height - 1):
 pos = 0, 20 + i * 20, 20, 20
 pygame.draw.rect(screen, color, pos, width)
 pos = (board_width - 1) * 20, 20 + i * 20, 20, 20
 pygame.draw.rect(screen, color, pos, width)

接著是game_over模組:

如何判斷誰咬到自身呢?可以利用python內建資料結構set:set這種資料結構中不能有重複元素。如果將list變成set之後,長度變短了,就說明list中有重複元素,即貪吃蛇咬到自己了。

# 遊戲失敗
def game_over(snack):
 broad_x, broad_y = snack.get_head()
 flag = 0
 old = len(snack.item)
 new = len(set(snack.item))
 # 遊戲失敗的兩種可能
 # 咬到自身
 if new < old:
 flag = 1
 # 撞到邊框
 if broad_x == 0 or broad_x == BOARDWIDTH - 1:
 flag = 1
 if broad_y == 0 or broad_y == BOARDHEIGHT - 1:
 flag = 1
 if flag:
 return True
 else:
 return False

接下來是遊戲初始化模組:

使用pygame模組需要使用pygame.init進行初始化。pygame.display.set_mode用來設定遊戲介面的大小。pygame.display.set_caption用來顯示遊戲標題。

# 遊戲初始化
def game_init():
 # pygame 初始化
 pygame.init()
 # 設定遊戲介面大小
 screen = pygame.display.set_mode((BOARDWIDTH * 20, BOARDHEIGHT * 20))
 # 設定遊戲標題
 pygame.display.set_caption('貪吃蛇遊戲')
 # sound = pygame.mixer.Sound(AUDIONAME)
 # channel = pygame.mixer.find_channel(True)
 # channel.play(sound)
 return screen

最後是遊戲主函式:

首先例項化貪吃蛇和食物。其次設定字型為SimHei,如果使用預設字型對中文的支援很不好。其次顯示遊戲介面,判斷遊戲是否失敗。如果失敗的話,就列印GAME OVER。否則就一直執行主函式。其中 pygame.event.get從佇列中獲取事件,也就是說必須先獲取事件,才能得到使用者的鍵盤輸入和其他操作,screen.fill用於填充螢幕,pygame.key.get_pressed用於獲取使用者的鍵盤輸入,pygame.display.update用來重新整理到之前的圖案,time.sleep用於控制重新整理的頻率。

# 開始遊戲
def game(screen):
 snack = Snack()
 food = Food()
 # 設定中文字型和大小
 font = pygame.font.SysFont('SimHei', 20)
 is_fail = 0
 while True:
 for event in pygame.event.get():
 if event.type == QUIT:
 exit()
 # 填充螢幕
 screen.fill((0, 0, 100))
 init_board(screen=screen)
 # 獲得使用者按鍵命令
 keys = pygame.key.get_pressed()
 press(keys, snack)
 # 遊戲失敗列印提示
 if is_fail:
 font2 = pygame.font.Font(None, 40)
 print_text(screen, font, 0, 0, text)
 print_text(screen, font2, 400, 200, "GAME OVER")
 # 遊戲主程序
 if not is_fail:
 enlarge = snack.eat_food(food)
 text = u"score: {} 更多精彩關注微信公眾號:python高效程式設計".format(score)
 print_text(screen, font, 0, 0, text)
 food.update(screen, enlarge, snack)
 snack.move(enlarge)
 is_fail = game_over(snack=snack)
 snack.draw(screen)
 # 遊戲重新整理
 pygame.display.update()
 time.sleep(0.1)

好了,我們的貪吃蛇教程就這樣結束了,其他零碎的知識點都在原始碼中。大家可以自己嘗試編寫自己的第一個貪吃蛇遊戲了,還可以給自己的貪吃蛇擴充套件各種各樣的功能。比如一邊播放音樂,一邊開始遊戲,或者編寫個更加美觀的貪吃蛇介面。