摸鱼宝典(一)——贪吃蛇游戏改版:贪吃龙 · 双龙戏珠小游戏(Python)
????????啊哈哈哈,最近閑來無事,隨手寫了個小游戲,在學習的時候摸魚和劃水那是必備啊!還等什么?快來體驗這款獨特的改版貪吃蛇 ——《貪吃龍 ·?雙龍戲珠》小游戲吧!
【Tip:源碼下載鏈接在文章最下面】
【游戲效果】
游戲是貪吃蛇的改版,怎么樣,效果還不錯吧?
游戲開始時有兩條“龍”,一條綠龍,一條黃龍,分別用 AWSD 和 JIKL 控制上下左右,按空格鍵開始游戲,每吃掉一個食物,吃掉食物的那條龍長度加一,總分加10,任意一條龍撞墻都會si 。
【教程環節】?
【完整源碼在后面,可以直接參考】?
主要思路
兩個類,一個是蛇類Snake,另一個是主類Main,實例化一個Snake類就可以添加一條蛇,我們的目標是兩條蛇(畢竟咱也只有兩只手),Main類的設定完全只是方便于寫代碼,讓代碼很清晰。Snake類有四個方法,move方法用于移動,update方法用于更新,eat方法用于判斷是否吃掉食物,dead方法用于判斷是否撞墻si亡;Main類有三個方法,start方法用于啟動游戲的一些功能,fresh方法用于更新畫面,move方法用于接收鍵盤的消息并傳給Snake類的實例化的move方法。
步驟一:引入模塊
from tkinter import *# 引入界面化編程模塊 from random import *# 引入隨機數模塊引入兩個模塊,一個是tkinter模塊,用于界面化編程,一個是random模塊,用于隨機產生貪吃蛇的食物。
步驟二:設置全局變量
Food,Score,Life = [None,None],0,1# 設置初始食物位置、初始分數、是否存活(1==True 0==False)一個是食物的位置列表,還有一個是分數,最后是生命,設置為全局變量是為了方便后面的代碼對其進行修改。
步驟三:Main主類初始化
class Main:'''### 主類---類似于C語言中main函數用類實現只是為了方便,在這個程序中沒其他作用'''def __init__(self):'''界面的初始化'''self.game = Tk()# 初始化界面self.game.title('貪吃龍!')# 界面標題self.game.geometry('600x600+300+50') #界面大小及位置(600×600像素,偏移屏幕左上角橫向300像素,縱向50像素)self.game.resizable(0,0)# 窗口橫向、縱向大小是否可以拉伸(0==False 1==True)self.canvas = Canvas(self.game,bg='black',highlightthickness=0)# 初始化畫布控件,背景黑色,高亮邊框厚度為0self.canvas.place(width=600,height=600)# 畫布控件左上角置于(0,0)[默認值] 寬600像素、高600像素self.canvas.create_text(300,200,text='貪吃龍·雙龍戲珠',font=('華文行楷',30,'bold'),fill='#666666')# 字樣1self.canvas.create_text(300,300,text='按 AWSD 操控綠龍\n按 JIKL 操控黃龍\n按<空格>鍵開始游戲',font=('微軟雅黑',20,'bold'),fill='#666666',justify='center')# 字樣2self.score = self.canvas.create_text(300,400,text='0',font=('微軟雅黑',30,'bold'),fill='#666666')# 得分字樣self.s1 = Snake(self.game,self.canvas,4,12,'springgreen')# 實例化第一條 Snake(類)self.s2 = Snake(self.game,self.canvas,25,12,'gold')# 實例化第二條 Snake(類) 【有興趣的可以實例化第N條】self.game.bind('<Key-space>',self.start)# 空格鍵鍵盤關聯 start 方法self.game.mainloop()# 消息事件循環編寫 __init__ 方法,設置窗口的基本特征,具體每行代碼的作用都寫在上面的注釋中了。那個?bind 關聯的 start 方法在后面會寫到。Snake 類也在后面會添加,這里先不具體解釋了。
步驟四:編寫Main類的start方法、move方法和fresh方法
def start(self,event:Event):# event 參數沒有用到但不能刪去,為了與前面形成關聯'''游戲啟動'''self.game.unbind('<Key-space>')# 取消空格鍵的關聯self.move()# 鍵盤關聯開始有反應self.fresh()# 游戲畫面開始更新按下空格鍵以執行start方法,為防止后面誤按空格鍵,故取消空格鍵的關聯。
def move(self):'''鍵盤關聯'''self.game.bind('<Key-a>',self.s1.move)# a 按鍵檢測self.game.bind('<Key-w>',self.s1.move)# w 按鍵檢測self.game.bind('<Key-s>',self.s1.move)# s 按鍵檢測self.game.bind('<Key-d>',self.s1.move)# d 按鍵檢測self.game.bind('<Key-j>',self.s2.move)# j 按鍵檢測self.game.bind('<Key-i>',self.s2.move)# i 按鍵檢測self.game.bind('<Key-k>',self.s2.move)# k 按鍵檢測self.game.bind('<Key-l>',self.s2.move)# l 按鍵檢測對于 AWSD 和 JIKL 八個鍵盤按鍵,都進行關聯并指向Snake類的實例化的move方法。
def fresh(self):'''畫面更新'''global Food,Score,Life# 聲明為全局變量,方便對它們的修改if Food == [None,None]:# 判斷界面上是不是沒有食物,沒有才會刷新食物x,y = randint(0,29),randint(0,29)# 隨機產生食物的坐標 (x*20,y*20)Food = [self.canvas.create_rectangle(20*x+1,20*y+1,20*(x+1)-1,20*(y+1)-1,fill='red',width=0),[x,y]]# 產生食物self.s1.update()# 更新 s1 的位置self.s2.update()# 更新 s2 的位置self.canvas.itemconfigure(self.score,text=str(Score))# 更新分數字樣if Life:self.game.after(int(100-Score**0.5),self.fresh)# 如果 Life 不為 0(False) 就繼續“更新”界面的像素大小為600×600,以20×20像素的方格為一個單位,建立一個虛擬坐標,其中0≤x≤29,0≤y≤29,最后一行代碼會在全局變量Life不為0時執行,自動循環執行fresh方法,間隔時間為int(100-Score**0.5),也就是說,間隔時間會隨著分數的提高而減小。
步驟五:Snake蛇類的初始化
class Snake:'''### 蛇類 ---實例化一個就可以實現一個單獨的蛇Example:`s1 = Snake(self.game,self.canvas,4,12,'springgreen')`就可以實現一個顏色為'springgreen',頭部左上角位置在(4×20,12*20)的“蛇”'''def __init__(self,tk:Tk,canvas:Canvas,x:int,y:int,color:str):self.game = tkself.canvas = canvasi1 = self.canvas.create_rectangle(20*x+1,20*y+1,20*(x+1)-1,20*(y+1)-1,width=0,fill='purple')# 初始化蛇頭i2 = self.canvas.create_rectangle(20*x+1,20*(y+1)+1,20*(x+1)-1,20*(y+2)-1,width=0,fill=color)# 初始化蛇身1i3 = self.canvas.create_rectangle(20*x+1,20*(y+2)+1,20*(x+1)-1,20*(y+3)-1,width=0,fill=color)# 初始化蛇身2i4 = self.canvas.create_rectangle(20*x+1,20*(y+3)+1,20*(x+1)-1,20*(y+4)-1,width=0,fill=color)# 初始化蛇身3i5 = self.canvas.create_rectangle(20*x+1,20*(y+4)+1,20*(x+1)-1,20*(y+5)-1,width=0,fill=color)# 初始化蛇身4self.snake = [[i1,'w'],[i2,'w'],[i3,'w'],[i4,'w'],[i5,'w']]# 將蛇的數據放入蛇數據列表方便分析self.head,self.tail,self.color = [x,y],[x,y+4],color# 記錄當前蛇頭位置、當前蛇尾位置、蛇身顏色這里只對蛇的數據列表中單個元素作一下說明,如 [i1,'w'] 中,第一個代表蛇的身體方格,而 ‘w’ 則表示該方格下一次更新的方向,‘w’ 代表向上,‘a’,‘s’ 和 ‘d’ 分別代表向左、向下和向右。
步驟六:Snake類的move方法和update方法
def move(self,event:Event):'''移動的區分'''if event.char in ['a','j']:self.snake[0][1] = 'a'# 按 a 或 j 鍵向左elif event.char in ['w','i']:self.snake[0][1] = 'w'# 按 w 或 i 鍵向上elif event.char in ['s','k']:self.snake[0][1] = 's'# 按 s 或 k 鍵向下elif event.char in ['d','l']:self.snake[0][1] = 'd'# 按 d 或 l 鍵向右event.char 代表鍵盤輸入的字符。
def update(self):'''更新蛇的位置'''for k in range(len(self.snake)-1,-1,-1):# 遍歷蛇數據列表(必須倒遍歷!)dx = 0 if self.snake[k][1] in ['w','s'] else 20 if self.snake[k][1] == 'd' else -20# 解析當前蛇身在水平方向應該的位移dy = 0 if self.snake[k][1] in ['a','d'] else 20 if self.snake[k][1] == 's' else -20# 解析當前蛇身在垂直方向應該的位移self.canvas.move(self.snake[k][0],dx,dy)# 更新當前蛇身位置if k:self.snake[k][1] = self.snake[k-1][1]# 更新當前蛇身下次前行的方向(就是更新為前一個的蛇身的方向)else:# else的情況只能是 k 為 0,也就是蛇頭位置hx,hy = self.head# 讀取舊蛇頭位置self.head = [hx+dx//20,hy+dy//20]# 更新蛇頭位置數據if k == len(self.snake)-1:# 判斷是否為蛇尾tx,ty = self.tail# 讀取舊蛇尾位置self.tail = [tx+dx//20,ty+dy//20]# 更新蛇尾位置數據self.dead()# 判定是否撞墻死亡self.eat()# 判定是否吃掉食物每次更新的時候,根據Snake類的蛇數據列表 self.snake?中每個小列表的第二項來確定該方格單元應移向哪個方向,更新完方格在界面上的位置之后,還要再次更新該小列表的第二項,將其改為下一次更新的方向,而這個方向就是前一個方格單元的移動方向,因此,該蛇數據列表需要倒著更新。
同時,在更新蛇數據列表的同時還要判斷更新循環中當前更新方格是否為蛇頭或蛇尾,更新到這兩個地方時,要同時更新蛇頭位置存儲列表?self.head 或者蛇尾位置存儲列表 self.tail。蛇頭位置存儲列表是用于 dead 方法(si亡判定)和 eat 方法(吃掉食物),蛇尾位置存儲列表用于 eat 方法為蛇增加長度。
最后,每次更新完后,進行si亡判定(dead方法)和吃掉食物的檢測(eat方法)。
步驟七:Snake類的dead方法和eat方法
def dead(self):'''檢測是否撞墻死亡,實則是坐標位置越界檢測'''global Life# 聲明全局變量,方便對其的修改x,y = self.head# 蛇頭的位置if not (0<=x<=29 and 0<=y<=29):# 檢測蛇頭位置是否越界(超出屏幕)Life = 0# Life 設為 0(False) 標識死亡self.canvas.create_text(300,250,text='You\nDead',fill='red',font=('華文新魏',100,'bold'),justify='center')# 產生死亡字樣讀取蛇頭位置,對其是否越出屏幕界限進行檢測,若是則設置全局變量 Life 為0,方便于使其他功能終止(如Main類中的fresh方法),同時顯示死亡字樣。
def eat(self):'''檢測是否吃掉食物,有點類似于碰撞檢測(但又沒有碰撞檢測那么復雜)'''global Food,Score# 聲明全局變量,方便對其的修改if self.head == Food[1]:# 當前蛇頭位置與食物位置重合,判定為吃掉食物self.canvas.delete(Food[0])# 刪去食物的圖像Food = [None,None]# 設置食物坐標為空,即屏幕中沒有食物了Score += 10# 得分加10x,y = self.tail# 讀取當前蛇尾位置,準備給蛇增加長度dx = 0 if self.snake[-1][1] in ['w','s'] else 20 if self.snake[-1][1] == 'd' else -20# 判斷水平方向上蛇的位移dy = 0 if self.snake[-1][1] in ['a','d'] else 20 if self.snake[-1][1] == 's' else -20# 判斷垂直方向上蛇的位移x -= dx//20;y -= dy//20# 解析新蛇尾應該的實際坐標self.tail = [x,y]# 更新蛇尾坐標為新的蛇尾坐標self.snake.append([self.canvas.create_rectangle(20*x+1,20*y+1,20*(x+1)-1,20*(y+1)-1,width=0,fill=self.color),self.snake[-1][1]])# 蛇數據列表加上新蛇尾的數據這里要注意的就是,要先刪除 Food 在屏幕上的顯示,再清空 Food 位置列表,讀取蛇尾位置,解析出新蛇尾下一次更新的方向,然后再蛇數據列表上,增加新蛇尾的數據,最后,不要忘了,更新蛇尾位置列表。
步驟八:實例化Main類,開始游戲
Main()放在程序代碼的最后,程序從這里開始運行!?
大家在看完源碼后,也可以自己修改源碼,弄出三條龍,四條龍,甚至更多哦!
【完整源碼】
from tkinter import *# 引入界面化編程模塊 from random import *# 引入隨機數模塊Food,Score,Life = [None,None],0,1# 設置初始食物位置、初始分數、是否存活(1==True 0==False)class Snake:'''### 蛇類 ---實例化一個就可以實現一個單獨的蛇Example:`s1 = Snake(self.game,self.canvas,4,12,'springgreen')`就可以實現一個顏色為'springgreen',頭部左上角位置在(4×20,12*20)的“蛇”'''def __init__(self,tk:Tk,canvas:Canvas,x:int,y:int,color:str):self.game = tkself.canvas = canvasi1 = self.canvas.create_rectangle(20*x+1,20*y+1,20*(x+1)-1,20*(y+1)-1,width=0,fill='purple')# 初始化蛇頭i2 = self.canvas.create_rectangle(20*x+1,20*(y+1)+1,20*(x+1)-1,20*(y+2)-1,width=0,fill=color)# 初始化蛇身1i3 = self.canvas.create_rectangle(20*x+1,20*(y+2)+1,20*(x+1)-1,20*(y+3)-1,width=0,fill=color)# 初始化蛇身2i4 = self.canvas.create_rectangle(20*x+1,20*(y+3)+1,20*(x+1)-1,20*(y+4)-1,width=0,fill=color)# 初始化蛇身3i5 = self.canvas.create_rectangle(20*x+1,20*(y+4)+1,20*(x+1)-1,20*(y+5)-1,width=0,fill=color)# 初始化蛇身4self.snake = [[i1,'w'],[i2,'w'],[i3,'w'],[i4,'w'],[i5,'w']]# 將蛇的數據放入蛇數據列表方便分析self.head,self.tail,self.color = [x,y],[x,y+4],color# 記錄當前蛇頭位置、當前蛇尾位置、蛇身顏色def eat(self):'''檢測是否吃掉食物,有點類似于碰撞檢測(但又沒有碰撞檢測那么復雜)'''global Food,Score# 聲明全局變量,方便對其的修改if self.head == Food[1]:# 當前蛇頭位置與食物位置重合,判定為吃掉食物self.canvas.delete(Food[0])# 刪去食物的圖像Food = [None,None]# 設置食物坐標為空,即屏幕中沒有食物了Score += 10# 得分加10x,y = self.tail# 讀取當前蛇尾位置,準備給蛇增加長度dx = 0 if self.snake[-1][1] in ['w','s'] else 20 if self.snake[-1][1] == 'd' else -20# 判斷水平方向上蛇的位移dy = 0 if self.snake[-1][1] in ['a','d'] else 20 if self.snake[-1][1] == 's' else -20# 判斷垂直方向上蛇的位移x -= dx//20;y -= dy//20# 解析新蛇尾應該的實際坐標self.tail = [x,y]# 更新蛇尾坐標為新的蛇尾坐標self.snake.append([self.canvas.create_rectangle(20*x+1,20*y+1,20*(x+1)-1,20*(y+1)-1,width=0,fill=self.color),self.snake[-1][1]])# 蛇數據列表加上新蛇尾的數據def dead(self):'''檢測是否撞墻死亡,實則是坐標位置越界檢測'''global Life# 聲明全局變量,方便對其的修改x,y = self.head# 蛇頭的位置if not (0<=x<=29 and 0<=y<=29):# 檢測蛇頭位置是否越界(超出屏幕)Life = 0# Life 設為 0(False) 標識死亡self.canvas.create_text(300,250,text='You\nDead',fill='red',font=('華文新魏',100,'bold'),justify='center')# 產生死亡字樣def update(self):'''更新蛇的位置'''for k in range(len(self.snake)-1,-1,-1):# 遍歷蛇數據列表(必須倒遍歷!)dx = 0 if self.snake[k][1] in ['w','s'] else 20 if self.snake[k][1] == 'd' else -20# 解析當前蛇身在水平方向應該的位移dy = 0 if self.snake[k][1] in ['a','d'] else 20 if self.snake[k][1] == 's' else -20# 解析當前蛇身在垂直方向應該的位移self.canvas.move(self.snake[k][0],dx,dy)# 更新當前蛇身位置if k:self.snake[k][1] = self.snake[k-1][1]# 更新當前蛇身下次前行的方向(就是更新為前一個的蛇身的方向)else:# else的情況只能是 k 為 0,也就是蛇頭位置hx,hy = self.head# 讀取舊蛇頭位置self.head = [hx+dx//20,hy+dy//20]# 更新蛇頭位置數據if k == len(self.snake)-1:# 判斷是否為蛇尾tx,ty = self.tail# 讀取舊蛇尾位置self.tail = [tx+dx//20,ty+dy//20]# 更新蛇尾位置數據self.dead()# 判定是否撞墻死亡self.eat()# 判定是否吃掉食物def move(self,event:Event):'''移動的區分'''if event.char in ['a','j']:self.snake[0][1] = 'a'# 按 a 或 j 鍵向左elif event.char in ['w','i']:self.snake[0][1] = 'w'# 按 w 或 i 鍵向上elif event.char in ['s','k']:self.snake[0][1] = 's'# 按 s 或 k 鍵向下elif event.char in ['d','l']:self.snake[0][1] = 'd'# 按 d 或 l 鍵向右class Main:'''### 主類---類似于C語言中main函數用類實現只是為了方便,在這個程序中沒其他作用'''def __init__(self):'''界面的初始化'''self.game = Tk()# 初始化界面self.game.title('貪吃龍!')# 界面標題self.game.geometry('600x600+300+50') #界面大小及位置(600×600像素,偏移屏幕左上角橫向300像素,縱向50像素)self.game.resizable(0,0)# 窗口橫向、縱向大小是否可以拉伸(0==False 1==True)self.canvas = Canvas(self.game,bg='black',highlightthickness=0)# 初始化畫布控件,背景黑色,高亮邊框厚度為0self.canvas.place(width=600,height=600)# 畫布控件左上角置于(0,0)[默認值] 寬600像素、高600像素self.canvas.create_text(300,200,text='貪吃龍·雙龍戲珠',font=('華文行楷',30,'bold'),fill='#666666')# 字樣1self.canvas.create_text(300,300,text='按 AWSD 操控綠龍\n按 JIKL 操控黃龍\n按<空格>鍵開始游戲',font=('微軟雅黑',20,'bold'),fill='#666666',justify='center')# 字樣2self.score = self.canvas.create_text(300,400,text='0',font=('微軟雅黑',30,'bold'),fill='#666666')# 得分字樣self.s1 = Snake(self.game,self.canvas,4,12,'springgreen')# 實例化第一條 Snake(類)self.s2 = Snake(self.game,self.canvas,25,12,'gold')# 實例化第二條 Snake(類) 【有興趣的可以實例化第N條】self.game.bind('<Key-space>',self.start)# 空格鍵鍵盤關聯 start 方法self.game.mainloop()# 消息事件循環def start(self,event:Event):# event 參數沒有用到但不能刪去,為了與前面形成關聯'''游戲啟動'''self.game.unbind('<Key-space>')# 取消空格鍵的關聯self.move()# 鍵盤關聯開始有反應self.fresh()# 游戲畫面開始更新def fresh(self):'''畫面更新'''global Food,Score,Life# 聲明為全局變量,方便對它們的修改if Food == [None,None]:# 判斷界面上是不是沒有食物,沒有才會刷新食物x,y = randint(0,29),randint(0,29)# 隨機產生食物的坐標 (x*20,y*20)Food = [self.canvas.create_rectangle(20*x+1,20*y+1,20*(x+1)-1,20*(y+1)-1,fill='red',width=0),[x,y]]# 產生食物self.s1.update()# 更新 s1 的位置self.s2.update()# 更新 s2 的位置self.canvas.itemconfigure(self.score,text=str(Score))# 更新分數字樣if Life:self.game.after(int(100-Score**0.5),self.fresh)# 如果 Life 不為 0(False) 就繼續“更新”def move(self):'''鍵盤關聯'''self.game.bind('<Key-a>',self.s1.move)# a 按鍵檢測self.game.bind('<Key-w>',self.s1.move)# w 按鍵檢測self.game.bind('<Key-s>',self.s1.move)# s 按鍵檢測self.game.bind('<Key-d>',self.s1.move)# d 按鍵檢測self.game.bind('<Key-j>',self.s2.move)# j 按鍵檢測self.game.bind('<Key-i>',self.s2.move)# i 按鍵檢測self.game.bind('<Key-k>',self.s2.move)# k 按鍵檢測self.game.bind('<Key-l>',self.s2.move)# l 按鍵檢測Main()# 類似于程序的入口【下載鏈接】
藍奏云:DoubleDragon.zip?密碼:8j4v
總結
以上是生活随笔為你收集整理的摸鱼宝典(一)——贪吃蛇游戏改版:贪吃龙 · 双龙戏珠小游戏(Python)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 叉积 微分 恒等式_微分几何(一)
- 下一篇: 男女通用的城府修练 城府不是有心眼,而是