Python迷宮游戲
1. 簡介
利用Python中的turtle庫設計出一個由鍵盤控制的迷宮,並可利用深度優先遍歷進行求解最優通關路徑。
2. 實驗環境
Pycharm
3. 各部分代碼詳解
(1) 定義迷宮關卡:
以二維數組的形式可以設置迷宮,這里以字符串數組代替,便於書寫,其中'X'對應的是牆體,'P'對應的是玩家,'G'對應的是金幣,空格對應的是可通過的道路。
#定義關卡列表
levels=[]
#定義第一關
le1=[
"XXXXXXXXXXXXXXXXXXXXXXXXX",
"XXX XXXXXX XXXXXXXXXXX",
"XXXP XXXXXXX XXXXXXXXXXX",
"XXX XXXXXXX XXXXXXXXXXX",
"FGG XXXX",
"XXXXXXX XXXX XXXXX XXXX",
"XXXXXXXXXXXX XXXXXE EXX",
"XXXXXXXXXXXX XXXXX XXX",
"XXXXXXXXXXXX XXXXX XX",
"XX XX",
"XXXX XXXXXX XXXX XXXXX",
"XXXX XXXXXX XXXXXXXXXXX",
"XXXXE XXXXXXXX",
"XXXXXXXXXXEXXXX XXXXXXXX",
"XXXXXXXXXXXXXXX XXXXXXXX",
"XXXXXXXXXXXXXXX XXEXXXXX",
"XX XXXXXXXX",
"XX XXXXXXXXXXXXXXXXXXXX",
"XX XXXXX X",
"XX XXXXXXXXXXXXX XXXXX",
"XX XXXXXXXXXXX XXXXX",
"XX XXXX X",
"XXXX X",
"XXXXXXXXXXXXXXXXXXXXXXXXX"
]
#定義第二關
le2=[
"XXXXXXXXXXXXXXXXXXXXXXXXX",
"XXX XXXXXX XXXXXXXXXXX",
"XXXP XXXXXXX XXXXXXXXXXX",
"XXX XXXXXXX XXXXXXXXXXX",
"XXX XXXX",
"XXXXXXX XXXX XXXXX XXXX",
"XXXXXXXGXXXX XXXXXE EXX",
"XXXXXXXXXXXX XXXXX XXX",
"XXXXXXXXXXXX XXXXX XX",
"XX XX",
"XXXX XXXXXX XXXX GXXXX",
"XXXX XXXXXX XXXXXXXXXXX",
"XXXXE XXXXXXXX",
"XXXXXXXXXXEXXXX XXXXXXXX",
"XXXXXXXXXXXXXXX XXXXXXXX",
"XXXXGXXXXXXXXXX XXEXXXXX",
"XX XXXXXXXX",
"XX XXXXXXXXXXXXXXXXXXXX",
"XX XXXXX X",
"XX XXXXXXXXXXXXX XXXXX",
"XX XXXXXXXXXXX XXXXX",
"XX XXXX X",
"XXXX F",
"XXXXXXXXXXXXXXXXXXXXXXXXX"
]
#存放入關卡列表中
levels.append(le1)
levels.append(le2)
(2) 利用海龜庫建立迷宮背景
import turtle as t
maze=t.Screen()
#700x700大小
maze.setup(700,700)
#背景設為黑色
maze.bgcolor('black')
(3) 注冊一下需要使用到的圖片
注意,圖片的格式是gif,圖片的大小是24x24,這取決於每個矩陣元素占多大的像素大小,和代碼文件放在同一個文件夾下。
#只有利用turtle.register_shape()函數注冊過后的圖片,才能作為后續畫筆的形狀。
#牆磚圖片
maze.register_shape("wall.gif")
#向右的小人
maze.register_shape("right.gif")
#向左的小人
maze.register_shape("left.gif")
#金幣圖片
maze.register_shape("glod.gif")
#這也是備選的小人
maze.register_shape("dog_left.gif")
maze.register_shape("dog_right.gif")
#終點旗子圖片
maze.register_shape("flag.gif")
順便加速一下作圖
#turtle.trace()函數可以加快作圖
maze.trace(0)
(4)定義金幣類和旗子類
在該游戲中,玩家需要吃到所有的金幣並走到終點旗子處才能通關,所以需要構建金幣類和旗子類,這兩個類也都是繼承turtle庫中的Turtle類的
#定義金幣類
class Glod(t.Turtle):
def __init__(self):
super().__init__()
#隱藏畫筆
self.ht()
#畫筆形狀為前面注冊過的金幣圖片
self.shape("glod.gif")
#畫筆移動速度拉滿
self.speed(0)
#因為是初始化,所以畫筆是抬起來的
self.penup()
#定義終點旗子類
class Flag(t.Turtle):
def __init__(self):
super().__init__()
self.ht()
#畫筆形狀為前面注冊過的旗子圖片
self.shape("flag.gif")
self.speed(0)
self.penup()
(5)定義一個玩家類
游戲的玩家需要有一些交互操作,比如得到移動和吃金幣的操作,所以玩家類中包含了向左、向右、向上、向下,吃金幣,通關等方法
#玩家類
class player(t.Turtle):
def __init__(self):
super().__init__()
self.ht()
#玩家初始默認向右,采用向右的人物形象
self.shape("dog_right.gif")
self.speed(0)
self.penup()
#前后左右移動函數
def go_r(self):
# print("you")
self.shape("dog_right.gif")
#畫筆坐標變化,向右就是x+24,注意+24是因為畫筆形狀大小是24*24
x=self.xcor()+24
y=self.ycor()
self.move(x,y)
def go_l(self):
# print("zuo")
self.shape("dog_left.gif")
x=self.xcor()-24
y=self.ycor()
self.move(x,y)
def go_u(self):
# print("shang")
x=self.xcor()
y=self.ycor()+24
self.move(x,y)
def go_d(self):
# print("xia")
x=self.xcor()
y=self.ycor()-24
self.move(x,y)
#移動函數
#前面的四個方法都是只計算出了下一步的坐標而沒有真正的跳轉到下一步,在move方法中進行移動
def move(self,x,y):
#walls為牆列表,里面的每個元素都是一個元組存放有各個牆體的坐標
#如果下一步是牆體,則不會進行移動並且給出撞牆了的提示,否則就進行移動
if (x,y) in walls:
print("撞牆了!")
else:
#移動角色
self.goto(x,y)
#吃金幣
self.eat()
#更新,turtle.update()進行所畫圖像的更新
maze.update()
#判斷是否通過
self.end()
#吃金幣函數
def eat(self):
#聲明全局變量score
global score
#遍歷整個金幣列表
for item in glods:
#turtle.distance()計算距離,這里計算玩家和金幣的距離
if item.distance(player) == 0:
#得分加1
score += 1
#將這個金幣從圖上隱藏,從視覺意義上消除
item.ht()
#將這個金幣從列表中清除,從物理意義上消除
glods.remove(item)
maze.update()
#target為目標得分,因為不需要更改它,所以不用全局聲明
print(f"你當前得分{score}/{target}")
#判斷通關函數
def end(self):
#temp作為通關的標志變量
global temp
#即吃完所有金幣,並且到達終點就算通關
if (score == target)and(flag.distance(player) == 0):
flag.ht()
temp=1
print(f"你當前得分{score}")
print("您已通關")
#show函數為顯示成功信息的函數,在后面進行定義
show("按回車進入下一關",pen2)
(6) 實例化和變量初始化
前面只是定義了玩家類和旗子類,並沒有實例化它們,在這里將其實例化一個對象出來,至於金幣類,我們在構建迷宮時再將其初始化。
#實例化玩家
player=player()
#實例化旗子
flag=Flag()
顯然注意到前面的類中有用到一些變量,我們在這里進行變量的定義
#建立得分機制
score = 0
target = 0
temp = 0
#當前關卡
now_level=1
#構建牆體數組
walls=[]
#構建金幣數組
glods=[]
(6) 定義一個畫筆類用來畫迷宮中的各種元素
這個pen類有一個初始化方法和一個構建迷宮的mazing方法,同時它是繼承了turtle庫中的Turtle類的
class pen(t.Turtle):
def __init__(self):
super().__init__()
#藏起畫筆
self.ht()
#畫筆形狀變成牆磚的圖片,這是前面注冊過的
self.shape("wall.gif")
#移動速度拉滿
self.speed(0)
#抬起畫筆
self.penup()
def mazing(self):
#該方法構建的迷宮是當前關卡,是在levels列表中的,由now_level變量進行控制
level=levels[now_level-1]
#遍歷數組
for i in range(len(level)):
row=level[i]
for j in range(len(row)):
#該坐標是進行過換算,如果畫布和畫筆大小改變也要隨之改變
x = -288 + 24*j
y = 288 - 24*i
#畫牆體
if row[j] == 'X':
#當下一位置是牆體時,將其坐標放入牆體數組中
walls.append((x,y))
#畫筆移動到下一坐標處
self.goto(x, y)
#在該位置留下印記
self.stamp()
#畫角色
if row[j]== 'P':
#將實例化的玩家移動到坐標處
player.goto(x,y)
#將之顯示出來,注意這里不是進行蓋章,而是進行turtle.st()畫筆顯示操作
#因為蓋章操作后的印記是不移動的,而玩家需要移動,故使用畫筆顯示操作
player.st()
#畫金幣
if row[j] == 'G':
#全局變量 target,每畫出一枚金幣,目標分數就加一
global target
target += 1
#實例化金幣對象
glod=Glod()
#將金幣對象加入到金幣列表,注意這里加入的不是坐標,而是對象
glods.append(glod)
#畫出金幣
glod.goto(x,y)
glod.st()
#畫旗子
if row[j] == 'F':
#同上
flag.goto(x,y)
flag.st()
(7) 構建迷宮
在定義好畫筆類之后,我們就可以進行迷宮的構建了
#實例化迷宮
pen=pen()
#調用pen類中的mazing方法,生產當前關卡對應的迷宮
pen.mazing()
(8)顯示提示信息
前面有提到,show函數,該函數是用來顯示提示信息的,注意這里需要輸入兩個參數,一個是想要打印的字符串,一個是調用的畫筆,所以還要定義一個畫筆
#提示信息的畫筆
pen2=t.Turtle()
#顯示提示信息函數
def show(str,pen2):
pen.ht()
pen.speed(0)
pen.penup()
#從此開始填充
pen.goto(-100,-150)
#填充出一個矩形框,填充色為#008c8c
pen.fillcolor("#008c8c")
pen.begin_fill()
#長邊為300像素,短邊為200像素
for i in range(4):
if i%2 ==0:
pen.fd(300)
else:
pen.fd(200)
pen.left(90)
pen.end_fill()
#分兩行,展示字符串
pen.goto(-80,-30)
pen.color("black")
pen.write("成功過關",font=('Arial',24,'bold'))
pen.goto(-80,-90)
pen.write(str,font=('Arial',24,'bold'))
具體效果如下圖:
當然可以自己展示更好看的配色和文字,在這里就獻丑了。
(9) 迭代進入下一關
前面我們定義的關卡列表顯然是可以保存很多關的,所以我們需要構建一個進入下一關的函數,讓玩家可以體驗到多個關卡。
#迭代下一關函數
def next_level():
#注意因為要修改下面的變量,所以要對其進行全局聲明
global score
global target
global temp
global now_level
global walls
global glods
#將牆體列表和金幣列表全部置空
walls=[]
glods=[]
#將畫筆清空,也就是牆體視覺效果全部清除
pen.clear()
#變量歸零
score = 0
target = 0
temp = 0
#判斷是否還有下一關
if now_level < len(levels):
#關卡加一
now_level += 1
#重新構建迷宮
pen.mazing()
else:
now_level = 1
print("游戲結束")
#展示游戲結束的提示信息
show("按回車重新開始游戲",pen2)
(10) 從鍵盤聽取指令
人物的移動顯然是需要依托於鍵盤的,而進入下一關的指令也需要回車鍵進行控制,所以在此利用海龜庫中的一些指令進行操作
#響應鍵盤
maze.listen()
#上下左右鍵對應玩家上下左右移動的函數
maze.onkey(player.go_r,'Right')
maze.onkey(player.go_l,'Left')
maze.onkey(player.go_u,'Up')
maze.onkey(player.go_d,'Down')
#回車鍵對應進入下一關的函數
maze.onkey(next_level,'Return')
(11)一點點結尾
創建了迷宮顯然要將其顯示出來
#打印迷宮
maze.title("我的迷宮")
maze.update()
maze.mainloop()
(12)小結
本次迷宮繪制,對於一個游戲來說還是相當粗糙的,沒有什么技術含量可言,不過對於熟悉使用面向對象的類操作來說,是一個不錯的練手機會,另外利用深度優先遍歷進行自動通關路徑的搜索,會在后面進行補充。
