您好,登錄后才能下訂單哦!
怎么在Python中使用tkinter模塊實現一個推箱子游戲?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
原理
1、地圖
地圖在思想方面沒有太大改變,還是和以前一樣使用二維數組表示。不過我認為這樣確實不是非常高效的做法,不過這個想法也是在我寫完之后才有的
2、移動
在移動方面我修改了很多遍,先是完全按照原先的算法。這個確實也實現了,不過只能在第一關有效,在我修改地圖之后發現了一系列問題,然后根據問題發現實際遇到的情況要復雜很多。因為Python是用強制縮進替代了{},所以代碼在觀看中會有些難度,希望大家見諒。
移動的思想大致如下:
/** * 0表示空白 * 1表示墻 * 2表示人 * 3表示箱子 * 4表示終點 * 5表示已完成的箱子 * 6表示在終點上的人 */ 一、人 1、移動方向為空白 前方設置為2 當前位置為0 2、移動方向為墻 直接return 3、移動方向為終點 前面設置為6 當前位置設置為0 4、移動方向為已完成的箱子 4.1、已完成箱子前面是箱子 return 4.2、已完成箱子前面是已完成的箱子 return 4.3、已完成箱子前面是墻 return 4.4、已完成箱子前面為空白 已完成箱子前面設置3 前方位置設置為6 當前位置設置為0 4.5、已完成箱子前面為終點 已完成箱子前面設置為5 前方位置設置為6 當前位置設置為0 5、前方為箱子 5.1、箱子前方為空白 箱子前方位置設置為3 前方位置設置為2 當前位置設置為0 5.2、箱子前方為墻 return 5.3、箱子前方為箱子 return 5.4、箱子前方為已完成的箱子 return 5.5、箱子前方為終點 箱子前方位置設置為5 前方位置設置為2 當前位置設置為0 二、在終點上的人 1、移動方向為空白 前方設置為2 當前位置設置為4 2、移動方向為墻 直接return 3、移動方向為終點 前面設置為6 當前位置設置為4 4、移動方向為已完成的箱子 4.1、已完成箱子前面是箱子 return 4.2、已完成箱子前面是已完成的箱子 return 4.3、已完成箱子前面是墻 return 4.4、已完成箱子前面為空白 已完成箱子前面設置3 前方位置設置為6 當前位置設置為4 4.5、已完成箱子前面為終點 已完成箱子前面設置為5 前方位置設置為6 當前位置設置為4 5、前方為箱子 5.1、箱子前方為空白 箱子前方位置設置為3 前方位置設置為2 當前位置設置為4 5.2、箱子前方為墻 return 5.3、箱子前方為箱子 return 5.4、箱子前方為已完成的箱子 return 5.5、箱子前方為終點 箱子前方位置設置為5 前方位置設置為2 當前位置設置為4
首先,人有兩種狀態,人可以站在空白處,也可以站在終點處。后面我發現,人在空白處和人在終點唯一的區別是,人移動后,人原先的位置一個設置為0,即空白,一個設置為4,即終點。所以我在移動前判斷人背后的東西,就可以省去一般的代碼了。上面的邏輯可以改為如下:
/** * 0表示空白 * 1表示墻 * 2表示人 * 3表示箱子 * 4表示終點 * 5表示已完成的箱子 * 6表示在終點上的人 */ if(當前位置為2): #即人在空白處 back = 0 elif(當前位置為6): #即人在終點處 back = 4 1、移動方向為空白 (可移動) 前方設置為2 當前位置為back 2、移動方向為墻 直接return 3、移動方向為終點 (可移動) 前面設置為6 當前位置設置為back 4、移動方向為已完成的箱子 4.1、已完成箱子前面是箱子 return 4.2、已完成箱子前面是已完成的箱子 return 4.3、已完成箱子前面是墻 return 4.4、已完成箱子前面為空白 (可移動) 已完成箱子前面設置3 前方位置設置為6 當前位置設置為back 4.5、已完成箱子前面為終點 (可移動) 已完成箱子前面設置為5 前方位置設置為6 當前位置設置為back 5、前方為箱子 5.1、箱子前方為空白 (可移動) 箱子前方位置設置為3 前方位置設置為2 當前位置設置為back 5.2、箱子前方為墻 return 5.3、箱子前方為箱子 return 5.4、箱子前方為已完成的箱子 return 5.5、箱子前方為終點 (可移動) 箱子前方位置設置為5 前方位置設置為2 當前位置設置為back
四、文件分析
目錄結構如下,主要有三個文件BoxGame、initGame和Painter。test文件的話就是測試用的,沒有實際用處。然后講一下各個文件的功能:
BoxGame:作為游戲的主入口,游戲的主要流程就在里面。老實說我Python學習的內容比較少,對Python的面向對象不是很熟悉,所有這個流程更偏向于面向過程的思想。
initGame:初始化或存儲一些數據,如地圖數據,人的位置,地圖的大小,關卡等
Painter:我在該文件里定義了一個Painter對象,主要就是用來繪制地圖
除此之外就是圖片資源和音樂資源了。
五、代碼分析
1、BoxGame
from tkinter import * from initGame import * from Painter import Painter from pygame import mixer #創建界面并設置屬性 #創建一個窗口 root = Tk() #設置窗口標題 root.title("推箱子") #設置窗口大小,當括號中為"widhtxheight"形式時,會判斷為設置寬高這里注意“x”是重要標識 root.geometry(str(width*step) + "x" + str(height*step)) #設置邊距, 當括號中為"+left+top"形式,會判斷為設置邊距 root.geometry("+400+200") #這句話的意思是width可以改變0,height可以改變0,禁止改變也可以寫成resizable(False, False) root.resizable(0, 0) #播放背景音樂 mixer.init() mixer.music.load('bgm.mp3') #加載音樂 mixer.music.play() #播放音樂,歌曲播放完會自動停止 #創建一個白色的畫板,參數分別是:父窗口、背景、高、寬 cv = Canvas(root, bg='white', height=height*step, width=width*step) #繪制地圖 painter = Painter(cv, map, step) painter.drawMap() #關聯Canvas cv.pack() #定義監聽方法 def move(event): pass #綁定監聽事件,鍵盤事件第一個參數固定為"<Key>",第二個參數為方法名(不能加括號) root.bind("<Key>", move) #進入循環 root.mainloop()
因為move的代碼比較長,就先不寫出來,后面講解。BoxGame主要流程如下:
導入模塊
創建窗口并設置屬性
播放背景音樂
創建畫板
在畫板上繪制地圖
將畫板鋪到窗口上
讓窗口關聯監聽事件
游戲循環了
2、initGame
#游戲需要的一些參數 mission = 0 mapList = [ [ [0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 1, 4, 1, 0, 0, 0], [0, 0, 1, 0, 1, 1, 1, 1], [1, 1, 1, 3, 0, 3, 4, 1], [1, 4, 0, 3, 2, 1, 1, 1], [1, 1, 1, 1, 3, 1, 0, 0], [0, 0, 0, 1, 4, 1, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0] ], [ [0, 0, 0, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 0, 0, 0, 0, 1, 0], [1, 1, 4, 0, 3, 1, 1, 0, 1, 1], [1, 4, 4, 3, 0, 3, 0, 0, 2, 1], [1, 4, 4, 0, 3, 0, 3, 0, 1, 1], [1, 1, 1, 1, 1, 1, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0] ], [ [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 4, 4, 1, 0, 0], [0, 1, 1, 0, 4, 1, 1, 0], [0, 1, 0, 0, 3, 4, 1, 0], [1, 1, 0, 3, 0, 0, 1, 1], [1, 0, 0, 1, 3, 3, 0, 1], [1, 0, 0, 2, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1] ], [ [1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 0, 0, 0, 1], [1, 0, 3, 4, 4, 3, 0, 1], [1, 2, 3, 4, 5, 0, 1, 1], [1, 0, 3, 4, 4, 3, 0, 1], [1, 0, 0, 1, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1] ] ] map = mapList[3] #人背后的東西 back = 0 #地圖的寬高 width, height = 0, 0 #地圖中箱子的個數 boxs = 0 #地圖中人的坐標 x = 0 y = 0 #畫面大小 step = 30 def start(): global width, height, boxs, x, y, map # 做循環變量 m, n = 0, 0 for i in map: for j in i: # 獲取寬,每次內循環的次數都是一樣的,只需要第一次記錄width就可以了 if (n == 0): width += 1 #遍歷到箱子時箱子數量+1 if (j == 3): boxs += 1 #當為2或者6時,為遍歷到人 if (j == 2 or j == 6): x, y = m, n m += 1 m = 0 n += 1 height = n start()
因為我還沒有實現關卡切換,所以這里的mapList和mission沒有太大用處,主要參數有一下幾個:
back:人背后的東西(前面分析過了)width、height:寬高boxs:箱子的個數x、y:人的坐標step:每個正方形格子的邊長,因為我對Canvas繪制圖片不熟悉,所以固定圖片為30px
因為initGame中沒有定義類,所以在引用時就相當于執行了其中的代碼。
3、Painter
from tkinter import PhotoImage, NW #在用Canvas繪制圖片時,圖片必須是全局變量 img = [] class Painter(): def __init__(self, cv, map, step): """Painter的構造函數,在cv畫板上,根據map畫出大小為step的地圖""" #傳入要拿來畫的畫板 self.cv = cv #傳入地圖數據 self.map = map #傳入地圖大小 self.step = step def drawMap(self): """用來根據map列表繪制地圖""" #img列表的長度 imgLen = 0 global img #循環變量 x, y = 0, 0 for i in self.map: for j in list(i): #記錄實際位置 lx = x * self.step ly = y * self.step # 畫空白處 if (j == 0): self.cv.create_rectangle(lx, ly, lx + self.step, ly+self.step, fill="white", width=0) # 畫墻 elif (j == 1): img.append(PhotoImage(file="imgs/wall.png")) self.cv.create_image(lx, ly, anchor=NW, image=img[imgLen - 1]) elif (j == 2): img.append(PhotoImage(file="imgs/human.png")) self.cv.create_image(lx, ly, anchor=NW, image=img[imgLen - 1]) # 畫箱子 elif (j == 3): img.append(PhotoImage(file="imgs/box.png")) self.cv.create_image(lx, ly, anchor=NW, image=img[imgLen - 1]) elif (j == 4): img.append(PhotoImage(file="imgs/terminal.png")) self.cv.create_image(lx, ly, anchor=NW, image=img[imgLen - 1]) elif (j == 5): img.append(PhotoImage(file="imgs/star.png")) self.cv.create_image(lx, ly, anchor=NW, image=img[imgLen - 1]) elif (j == 6): img.append(PhotoImage(file="imgs/t_man.png")) self.cv.create_image(lx, ly, anchor=NW, image=img[imgLen - 1]) x += 1 x = 0 y += 1
這里說一下,cv的方法,這里用到了兩個,一個是create_image一個是create_rectangle:
#繪畫矩形 cv.create_rectangle(sx, sy, ex, ey, key=value...) 1、前兩個參數sx、sy(s代表start)為左上角坐標 2、后兩個參數ex、ey(e代表end)表示右下角坐標 3、而后面的key=value...表示多個key=value形式的參數(順序不固定) 如: #填充色為紅色 fill = "red" #邊框色為黑色 outline = "black" #邊框寬度為5 width = 5 具體使用例如: #在左上角畫一個邊長為30,的黑色矩形 cv.create_rectangle(0, 0, 30, 30, fill="black", width=0)
然后是繪制圖片:
#這里要注意img必須是全局對象 self.cv.create_image(x, y, anchor=NW, img) 1、前兩個參數依舊是坐標,但是這里不一定是左上角坐標,x,y默認是圖片中心坐標 2、anchor=NW,設置anchor后,x,y為圖片左上角坐標 3、img是一個PhotoImage對象(PhotoImage對象為tkinter中的對象),PhotoImage對象的創建如下 #通過文件路徑創建PhotoImage對象 img = PhotoImage(file="img/img1.png")
因為我自己也不是非常了解,所以更細節的東西我也說不出來了。
然后是實際坐標的問題,上面說的坐標都是以數組為參考。而實際繪圖時,需要用具體的像素。在繪制過程中,需要繪制兩種,矩形、圖片。
矩形:矩形需要兩個坐標。當數組坐標為(1,1)時,因為單元的間隔為step(30),所以對應的像素坐標為(30, 30)。(2,2)對應(60,60),即(x*step,y*step),而終點位置為(x*step+step,y*step+step)。
圖片:繪制圖片只需要一個坐標,左上角坐標,這個是前面一樣為(x*step, y*step)。
上面還有一個重要的點,我在最開始定義了img列表,用于裝圖片對象。開始我嘗試用單個圖片對象,但是在繪制圖片的時候只會顯示一個,后面想到用img列表代替,然后成功了。(因為我學的不是非常扎實,也解釋不清楚)。
在繪制圖片時有以下兩個步驟:
#根據數組元素,創建相應的圖片對象,添加到列表末尾 img.append(PhotoImage(file="imgs/wall.png")) #在傳入圖片對象參數時,使用img[imgLen - 1],imgLen為列表當前長度,而imgLen-1就是最后一個元素,即剛剛創建的圖片對象 self.cv.create_image(lx, ly, anchor=NW, image=img[imgLen - 1])
4、move
def move(event): global x, y, boxs, back, mission,mapList, map direction = event.char #判斷人背后的東西 # 在空白處的人 if (map[y][x] == 2): back = 0 #講back設置為空白 # 在終點上的人 elif (map[y][x] == 6): back = 4 #將back設置為終點 #如果按的是w if(direction == 'w'): #獲取移動方向前方的坐標 ux, uy = x, y-1 #如果前方為墻,直接return if(map[uy][ux] == 1): return # 前方為空白(可移動) if (map[uy][ux] == 0): map[uy][ux] = 2 #將前方設置為人 # 前方為終點 elif (map[uy][ux] == 4): map[uy][ux] = 6 #將前方設置為終點 # 前方為已完成的箱子 elif (map[uy][ux] == 5): #已完成箱子前面為箱子已完成箱子或者墻都不能移動 if (map[uy - 1][ux] == 3 or map[uy - 1][ux] == 5 or map[uy - 1][ux] == 1): return # 已完成前面為空白(可移動) elif (map[uy - 1][ux] == 0): map[uy - 1][ux] = 3 #箱子向前移動 map[uy][ux] = 6 #已完成箱子處原本是終點,人移動上去之后就是6了 boxs += 1 #箱子移出,箱子數量要+1 #已完成箱子前面為終點(可移動) elif (map[uy - 1][ux] == 4): map[uy - 1][ux] = 5 #前方的前方設置為已完成箱子 map[uy][ux] = 6 #前方的箱子處原本是終點,人移動上去后是6 # 前方為箱子 elif (map[uy][ux] == 3): # 箱子不能移動 if (map[uy - 1][ux] == 1 or map[uy - 1][ux] == 3 or map[uy - 1][ux] == 5): return # 箱子前方為空白 elif (map[uy - 1][ux] == 0): map[uy - 1][ux] = 3 map[uy][ux] = 2 # 箱子前方為終點 elif (map[uy - 1][ux] == 4): map[uy - 1][ux] = 5 map[uy][ux] = 2 boxs -= 1 #前面只是改變了移動方向的數據,當前位置還是2或6,此時把當前位置設置為back map[y][x] = back #記錄移動后的位置 y = uy # 清除屏幕,并繪制地圖 cv.delete("all") painter.drawMap() if(boxs == 0): print("游戲結束")
看完上述內容,你們掌握怎么在Python中使用tkinter模塊實現一個推箱子游戲的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。