Python迷宫游戏

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'))

​ 具体效果如下图:

image-20211113151530616

​ 当然可以自己展示更好看的配色和文字,在这里就献丑了。

(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)小结

​ 本次迷宫绘制,对于一个游戏来说还是相当粗糙的,没有什么技术含量可言,不过对于熟悉使用面向对象的类操作来说,是一个不错的练手机会,另外利用深度优先遍历进行自动通关路径的搜索,会在后面进行补充。

posted on 2021-11-13 15:32  QSun  阅读(1000)  评论(1编辑  收藏  举报