利用python實現(xiàn)簡易版的貪吃蛇游戲(面向python小白)
引言
作為python 小白,總是覺得自己要做好百分之二百的準(zhǔn)備,才能開始寫程序。以至于常常整天在那看各種語法教程,學(xué)了幾個月還是只會print('hello world')。
這樣做效率太低,正確的做法,是到身邊找問題,然后編程實現(xiàn)。比如說,我學(xué)了高等數(shù)學(xué),我是不是應(yīng)該考慮下如何去用編程實現(xiàn)求導(dǎo)或者積分操作,如果想不出怎么辦,是不是應(yīng)該 baidu 一下,別人是如何實現(xiàn)數(shù)值積分或是符號積分的。我們每天買東西都要用到加減甚至乘除,那么我是否能編寫個簡單的計算器,如果命令行太丑的話,我是否能夠快速地學(xué)一學(xué) pyqt5或是其他 gui來實現(xiàn)精致些的應(yīng)用程序。凡事用編程思維考慮一下,對于從編程小白進階為編程入門是大有裨益的。
小時候,我們或多或少會沉迷于一款經(jīng)久不衰的游戲------貪吃蛇?;蛟S我們玩過各式各樣的貪吃蛇游戲,卻沒有自己動手編寫屬于自己的貪吃蛇游戲。今天就讓我們走進貪吃蛇的世界,用 python 實現(xiàn)簡易版的貪吃蛇游戲。
游戲簡介
首先是游戲效果圖:
用戶通過操控貪吃蛇,去吃到盡可能多的食物。其中貪吃蛇不能碰到墻壁,也不能咬到自身。
本教程借助 pygame實現(xiàn)游戲界面,所以下面稍稍介紹一下 pygame的安裝,用法就在下面連同函數(shù)一起講了:
安裝:
pip install -U pygame
接下來讓我們介紹下實現(xiàn)貪吃蛇的關(guān)鍵邏輯:
貪吃蛇的身體是由list構(gòu)成的,list中每一個元組代表貪吃蛇在棋盤上的坐標(biāo),我們只需在這些位置畫上圖案,就能制作出一條圓滾滾的貪吃蛇來。但是如果想讓貪吃蛇活蹦亂跳,我們就要寫一個move函數(shù)。
那么貪吃蛇怎么移動呢?
如果貪吃蛇沒吃到食物,那么我們就刪除list中最后一個坐標(biāo),再在蛇頭部分插入新的位置。如何確定新的位置呢,我們就要設(shè)定貪吃蛇移動的方向(x,y),將原蛇頭位置的坐標(biāo)在移動方向上進行加減操作。這樣貪吃蛇就實現(xiàn)了向前移動的目標(biāo)。如果貪吃蛇恰好吃到了食物,唯一的不同就是不需要刪除貪吃蛇尾部的元素。其中需要注意的是,貪吃蛇不能朝著當(dāng)前移動方向的反方向移動。體現(xiàn)在代碼中,就是當(dāng)前方向與改變方向的乘積不能為負(fù)值。
那么如何知道貪吃蛇吃到了食物呢?
如果貪吃蛇蛇頭的坐標(biāo)與食物的坐標(biāo)重合的話,貪吃蛇就吃到了食物。如果貪吃蛇吃到了食物,就在棋盤上隨機更新食物。如果隨機生成的食物的坐標(biāo),恰好與貪吃蛇的位置重合的話,就繼續(xù)隨機產(chǎn)生坐標(biāo),直到確保與貪吃蛇的坐標(biāo)不同的時候。
那么如何知道游戲失敗了呢?
如果貪吃蛇蛇頭的坐標(biāo)與邊框的坐標(biāo)重合的話,蛇卒。如果貪吃蛇各個部分的坐標(biāo)有重合的話,就說明貪吃蛇咬到了自己,游戲結(jié)束。
接下來是各個部分的具體代碼實現(xiàn):
下圖為主要需要的幾個函數(shù):
首先來看貪吃蛇模塊:
首先__init__初始化貪吃蛇的位置,初始方向豎直向上。toward函數(shù)用于改變貪吃蛇的方向,(x,y)分別表示蛇頭在水平和豎直方向的朝向。朝左x=-1,朝右x=1,朝上y=-1,朝下y=1。move函數(shù),使用標(biāo)志enlarge來判斷蛇是否吃到了食物,并進行相應(yīng)的操作。eat_food函數(shù)判斷蛇是否吃到食物,吃到的話,分?jǐn)?shù)加 100,并返回True。toward函數(shù),用于改變蛇頭的方向,但如果改變方向與當(dāng)前方向相反,就什么操作都不執(zhí)行。draw函數(shù)用于畫出貪吃蛇的模樣,蛇頭是略大一點的紅心,蛇身是小一點的黃心。
我們怎么畫出這條蛇呢?這就要借助函數(shù)pygame.draw.circle,這個函數(shù)的主要參數(shù)有screen:就是你要在其中畫出貪吃蛇的游戲界面,color:圖案的顏色(RGB), position:圖案在屏幕上的位置, radius:的半徑,width:內(nèi)部填色的大小,如果為零,圖案就是空心圓;如果與半徑大小相同,圖案就是實心圓。
下面是貪吃蛇的代碼部分,大家可以結(jié)合注釋閱讀:
# 貪吃蛇 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 標(biāo)記貪吃蛇有沒有吃到食物 if not enlarge: # 吃到食物刪除尾部元素 self.item.pop() # 新蛇頭的坐標(biāo)為舊蛇頭坐標(biāo)加上移動方向的位移 head = (self.item[0][0] + self.x, self.item[0][1] + self.y) # 將新的蛇頭坐標(biāo)插入在 list 最前面 self.item.insert(0, head) def eat_food(self, food): global score # snack_x,snack_y 蛇頭坐標(biāo) # food_x, food_y 食物坐標(biāo) snack_x, snack_y = self.item[0] food_x, food_y = food.item # 比較蛇頭坐標(biāo)與食物坐標(biāo) 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): # 獲取蛇頭坐標(biāo) return self.item[0] def draw(self, screen): # 畫出貪吃蛇 # 蛇頭為半徑為 15 的紅色實心圓 radius = 15 width = 15 # i:1---34 j:1---25 color = 255, 0, 0 # position 為圖形的坐標(biāo) 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用于產(chǎn)生邊界之內(nèi)的坐標(biāo),如果與貪吃蛇的坐標(biāo)重合,那么就繼續(xù)生成新的隨機坐標(biāo)。
# 食物 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) # 隨機產(chǎn)生食物 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函數(shù):
board_width、board_height分別為游戲界面的寬度和高度,根據(jù)計算得出邊框占據(jù)的位置,然后打印出正方形來。pygame.draw.rect和pygame.draw.circle用法類似,區(qū)別就是rect四個參數(shù)分別為screen:屏幕,color:顏色,pos:橫坐標(biāo) x,縱坐標(biāo) y,矩形的長,矩形的寬。這里我設(shè)置矩形長寬都為 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內(nèi)置數(shù)據(jù)結(jié)構(gòu)set:set這種數(shù)據(jù)結(jié)構(gòu)中不能有重復(fù)元素。如果將list變成set之后,長度變短了,就說明list中有重復(fù)元素,即貪吃蛇咬到自己了。
# 游戲失敗 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用來設(shè)置游戲界面的大小。pygame.display.set_caption用來顯示游戲標(biāo)題。
# 游戲初始化 def game_init(): # pygame 初始化 pygame.init() # 設(shè)置游戲界面大小 screen = pygame.display.set_mode((BOARDWIDTH * 20, BOARDHEIGHT * 20)) # 設(shè)置游戲標(biāo)題 pygame.display.set_caption('貪吃蛇游戲') # sound = pygame.mixer.Sound(AUDIONAME) # channel = pygame.mixer.find_channel(True) # channel.play(sound) return screen
最后是游戲主函數(shù):
首先實例化貪吃蛇和食物。其次設(shè)置字體為SimHei,如果使用默認(rèn)字體對中文的支持很不好。其次顯示游戲界面,判斷游戲是否失敗。如果失敗的話,就打印GAME OVER。否則就一直執(zhí)行主函數(shù)。其中 pygame.event.get從隊列中獲取事件,也就是說必須先獲取事件,才能得到用戶的鍵盤輸入和其他操作,screen.fill用于填充屏幕,pygame.key.get_pressed用于獲取用戶的鍵盤輸入,pygame.display.update用來刷新到之前的圖案,time.sleep用于控制刷新的頻率。
# 開始游戲 def game(screen): snack = Snack() food = Food() # 設(shè)置中文字體和大小 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: {} 更多精彩關(guān)注微信公眾號: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)
好了,我們的貪吃蛇教程就這樣結(jié)束了,其他零碎的知識點都在源碼中。大家可以自己嘗試編寫自己的第一個貪吃蛇游戲了,還可以給自己的貪吃蛇擴展各種各樣的功能。比如一邊播放音樂,一邊開始游戲,或者編寫個更加美觀的貪吃蛇界面。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
解讀殘差網(wǎng)絡(luò)(Residual Network),殘差連接(skip-connect)
這篇文章主要介紹了殘差網(wǎng)絡(luò)(Residual Network),殘差連接(skip-connect),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08淺談python print(xx, flush = True) 全網(wǎng)最清晰的解釋
今天小編就為大家分享一篇淺談python print(xx, flush = True) 全網(wǎng)最清晰的解釋,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02