|
三、最后的案例Turtle小游戏
1.设计思路游戏规则
这个海龟闯关的小迷宫游戏是设计了三个关卡,运行代码之后会有相应的关卡弹窗设置,想完那关可以直接玩那关, 也可以按照顺序闯关。
特别注意:
很多人担心过不了关?没关系,小编给大家开了一个挂,这个挂就是按住F1就出现了迷宫的路线图哦!按住F2就是出现了一个海龟到起点自己走路线哈!
2.准备环节
小编用的Python3、Pycharm2021、Turtle模块导入即可。
然后相应的图片素材:
3.正式敲代码
# -*- coding: UTF-8 -*-
"""
源码基地:#806965976#
csdn账号:顾木子吖
海龟迷宫闯关游戏
"""
import turtle # 导入海龟绘图模块
import random # 导入随机数模块
game_title = '小海龟大迷宫闯关小游戏' # 游戏名字
level = 0 # 关卡
'''绘制地图用的全局变量'''
txt_path = 'map/map1.txt' # 地图信息文本文件路径及名称
road_color = (191, 217, 225) # 迷宫通道的颜色
R, C = 0, 0 # 迷宫地图的总行数R、总列数C
cell_size = 20 # 一个格子的尺寸
area_sign = {} # 记录入口和出口索引位置
mazeList = [] # 地图列表
'''海龟对象'''
map_t = turtle.Turtle() # 绘制地图的海龟
map_t.speed(0) # 设置绘图速度最快(地图绘制)
sign_t = turtle.Turtle() # 绘制入口和出口标记的海龟
auto_t = turtle.Turtle() # 自动走迷宫的海龟
auto_t.pensize(5) # 画笔粗细(自动)
auto_t.speed(0) # 设置绘图速度最快(手动)
auto_t.ht() # 隐藏海龟光标
manual_t = turtle.Turtle() # 手动走迷宫的海龟
manual_t.pensize(5) # 画笔粗细(手动)
manual_t.speed(0) # 设置绘图速度最快(手动)
manual_t.shape('turtle') # 设置海龟光标为小海龟(手动)
manual_t.ht() # 隐藏手动走迷宫所用的海龟光标(手动)
# 要探索4个方向对应索引的变化规则
direction = [
(1, 0), # 右
(-1, 0), # 左
(0, 1), # 上
(0, -1) # 下
]
def imoveto(ci, ri):
"""
功能:根据索引位置移动海龟(不画线)
:param ci: 列索引
:param ri: 行索引
:return:
"""
auto_t.penup() # 抬笔
cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置
auto_t.goto(cx, cy) # 移动到指定位置
auto_t.pendown() # 落笔
auto_t.shape('turtle') # 设置海龟光标的形状
auto_t.color('red') # 设置画笔颜色为红色
auto_t.st() # 显示海龟光标
def c_move_to(t, ctuple): # 移动到指定位置
"""
功能:根据坐标位置移动到指定位置(不画线)
:param t: 海龟对象
:param ctuple: 记录坐标位置的元组
:return:
"""
t.ht() # 隐藏海龟光标
t.penup() # 抬笔
t.goto(ctuple[0], ctuple[1]) # 移动到坐标指定的位置
t.pendown() # 落笔
def itoc(ituple):
"""
将索引位置转换为实际坐标位置
:param ituple: 行、列索引组成的元组
:return: 实际坐标位置
"""
ci = ituple[0]
ri = ituple[1]
tx = ci * cell_size - C * cell_size / 2 # 根据索引值计算每个正方形的起点(x坐标)
ty = R * cell_size / 2 - ri * cell_size # 根据索引值计算每个正方形的起点(y坐标)
cx = tx + cell_size / 2 # 正方形中心的x坐标
cy = ty - cell_size / 2 # 正方形中心的y坐标
return (cx, cy)
def ctoi(cx, cy):
"""
根据cx和cy求在列表中对应的索引
:param cx: x轴坐标
:param cy: y轴坐标
:return: 元组,(ci,ri)
"""
ci = ((C - 1) * cell_size / 2 + cx) / cell_size # 计算列索引
ri = ((R - 1) * cell_size / 2 - cy) / cell_size # 计算行索引
return (int(ri), int(ci)) # 返回行列索引的元组
def get_map(filename):
"""
功能:读取保存地图的文本文件内容到列表
:param filename: 地图文件名
:return: 地图列表
"""
with open(filename, 'r') as f: # 打开文件
fl = f.readlines() # 读取全部行
maze_list = [] # 保存地图的列表
for line in fl: # 将读取的内容以空格分割为二维列表
line = line.strip() # 去掉空格
line_list = line.split(" ") # 以空格进行分割为列表
maze_list.append(line_list) # 将分割后的列表添加到地图列表中
return maze_list # 返回地图列表
def draw_square(ci, ri, colorsign):
"""
功能:绘制组成地图的小正方形
:param ci: 列索引
:param ri: 行索引
:param colorsign: 填充颜色
:return:
"""
tx = ci * cell_size - C * cell_size / 2 # 根据索引值计算每个正方形的起点(x坐标)
ty = R * cell_size / 2 - ri * cell_size # 根据索引值计算每个正方形的起点(y坐标)
map_t.penup() # 抬笔
map_t.goto(tx, ty) # 移动到绘图起点(正方形的左上角)
if colorsign == '1': # 判断是否为墙(如果为墙,则随机生成填充颜色)
r = random.randint(100, 130) # 红色值
g = random.randint(150, 180) # 绿色值
map_t.color(r, g, 200) # 指定颜色为随机生成的颜色
else:
map_t.color(colorsign) # 设置为指定的通道颜色
map_t.pendown() # 落笔
map_t.begin_fill() # 填充开始
for i in range(4): # 绘制正方形
map_t.fd(cell_size)
map_t.right(90)
map_t.end_fill() # 填充结束
map_t.ht() # 隐藏海龟光标
def draw_map(mazelist):
"""
功能:遍历地图列表绘制迷宫地图
:param mazelist: 保存地图数据的列表
:return:
"""
turtle.tracer(0) # 隐藏动画效果
global area_sign # 全局变量,记录入口和出口索引位置
for ri in range(R): # 遍历行
for ci in range(C): # 遍历列
item = mazelist[ri][ci]
if item in ['1']: # 判断墙
draw_square(ci, ri, '1') # 绘制墙
elif item == "S": # 判断入口
draw_square(ci, ri, road_color) # 绘制通道
draw_sign(ci - 1, ri, '入口') # 标记入口
area_sign['entry_i'] = (ci, ri) # 保存入口索引
elif item == "E": # 判断出口
draw_square(ci, ri, road_color) # 绘制通道
draw_sign(ci - 1, ri, '出口') # 标记出口
area_sign['exit_i'] = (ci, ri) # 保存出口索引
else:
draw_square(ci, ri, road_color) # 绘制通道
turtle.tracer(1) # 显示动画效果
def draw_sign(ci, ri, word):
"""
功能:绘制入口和出口标记
:param ci: 列索引
:param ri: 行索引
:param word: 标记文字内容
:return:
"""
cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置
sign_t.ht() # 隐藏海龟光标
sign_t.penup() # 抬笔
sign_t.goto(cx, cy) # 移动到标记位置
sign_t.color('red') # 设置画笔为红色
sign_t.write(word, font=('黑体', 12, 'normal')) # 绘制标记文字
def win_tip():
"""
功能:制作过关提示
:return:
"""
global level
c_move_to(manual_t, (-150, 0))
manual_t.color('blue')
if int(level) == 3:
manual_t.write('\n恭喜您顺利通关!', font=('黑体', 20, 'bold'))
turtle.onkey(turtle.bye, key='Return') # 监听按下Enter键退出游戏
else:
manual_t.write('\n恭喜过关!\n按下Enter进入下一关!', font=('黑体', 20, 'bold'))
level += 1
manual_t.color('red')
turtle.onkey(level_init, key='Return') # 监听按下Enter键
def manual_move(d):
"""
功能:手动走迷宫时通用探索并移动函数
:param d: 向不同方面走时索引的变化规则
:return:
"""
dc, dr = d # 将表示方向的元组分别赋值给两个变量dc和dr,其中dc为x轴方向,dr为y轴方向
rici = ctoi(round(manual_t.xcor(), 1) + dc * cell_size, round(manual_t.ycor(), 1) + dr * cell_size) # 获取行列索引
point = mazeList[rici[0]][rici[1]] # 获取地图列表中对应点的值
print('移动:', rici, point)
if point == '0': # 通路
manual_t.color('red')
mazeList[rici[0]][rici[1]] = '$' # 将当前位置标记为已探索
manual_t.forward(cell_size) # 向前移动
print('00')
elif point == '$': # 已探索
manual_t.color(road_color) # 绘制和通道相同颜色的线,达到擦除痕迹的效果
mazeList[rici[0] + dr][rici[1] - dc] = '0' # 将当前位置的前一个点设置为未探索(目的是取消标记)
manual_t.forward(road_color) # 向前移动
manual_t.color('red')
elif point == 'E': # 出口
win_tip()
def up_move(): # 朝上
manual_t.setheading(90) # 设置海龟朝向
manual_move(direction[2]) # 手动探索并移动
def down_move(): # 朝下
manual_t.setheading(270) # 设置海龟朝向
manual_move(direction[3]) # 手动探索并移动
def left_move(): # 朝左
manual_t.setheading(180) # 设置海龟朝向
manual_move(direction[1]) # 手动探索并移动
def right_move(): # 朝右
manual_t.setheading(0) # 设置海龟朝向
manual_move(direction[0]) # 手动探索并移动
def manual_path():
"""
功能:手动走迷宫
:return:
"""
manual_t.clear() # 清除绘图
auto_t.ht() # 隐藏海龟
auto_t.clear() # 清除绘图
global mazeList # 定义全局变量
mazeList = get_map(txt_path) # 重新读取地图数据
# print(area_sign['entry_i'][0],area_sign['entry_i'][1])
c_move_to(manual_t, itoc(area_sign['entry_i'])) # 移动到入口位置
manual_t.st() # 显示手动走迷宫所用的海龟光标
manual_t.width(3) # 设置画笔粗细为3像素
manual_t.color('red') # 设置画笔为红色
manual_t.getscreen().listen() # 让海龟屏幕(TurtleScreen)获得焦点
manual_t.getscreen().onkeyrelease(up_move, 'Up') # 按下向上方向键
manual_t.getscreen().onkeyrelease(down_move, 'Down') # 按下向下方向键
manual_t.getscreen().onkeyrelease(left_move, 'Left') # 按下向左方向键
manual_t.getscreen().onkeyrelease(right_move, 'Right') # 按下向右方向键
def auto_path():
"""
功能:查看答案(自动走迷宫)
:return:
"""
global mazeList # 定义全局变量
mazeList = get_map(txt_path) # 重新读取地图数据
manual_t.ht() # 隐藏海龟
manual_t.clear() # 清除绘图
auto_t.clear() # 清除绘图
auto_t.pensize(5) # 设置画笔粗细
auto_t.speed(0) # 绘图速度
auto_t.ht() # 隐藏海龟光标
find(mazeList) # 开始探索
def find(mazeList):
"""
功能:开始探索
:param mazeList: 地图列表
:return:
"""
auto_t.clear() # 清空帮助
start_r, start_c = 0, 0
for ri in range(R):
for ci in range(C):
item = mazeList[ri][ci]
if item == "S":
start_r, start_c = ri, ci
auto_t.penup() # 抬笔
draw_path(start_c, start_r)
find_next(mazeList, start_c, start_r)
def find_next(mlist, ci, ri):
"""
功能:递归搜索判断是否为通路
:param mlist: 地图列表
:param ci: 列索引
:param ri: 行索引
:return: 布尔值,表示是否为通路
"""
if mlist[ri][ci] == "E":
imoveto(ci, ri) # 移动到出口
return True
if not (0 <= ci < C and 0 <= ri < R): # 判断位置是否不合法
return False
if mlist[ri][ci] in ['1', '$']: # 判断是否为墙或者已探索过的
return False
mlist[ri][ci] = "$" # 标记已探索过
for d in direction: # 尝试从不同方向探索是否为通路,如果发现一条通路,则不再继续探索
dc, dr = d # # 将索引变化规则的值分别赋值给dc和dr,其中dc为x轴方向,dr为y轴方向
found = find_next(mlist, ci + dc, ri + dr) # 递归调用
if found: # 如果是通路则绘制线路
draw_path(ci, ri) # 绘制线路
return True # 返回True,不再探索
return False # 当所有方向都不通时,返回False
def draw_path(ci, ri, color="green"): # 自动绘制用
"""
功能:根据索引位置移动海龟(画线)
:param ci: 列索引
:param ri: 行索引
:param color: 画笔颜色
:return:
"""
auto_t.st() # 显示海龟光标
cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置
auto_t.color(color)
auto_t.goto(cx, cy)
def level_init():
"""
功能:关卡初始化
游戏规则:
按下F2键开始手动走迷宫;按下F1键查看答案
按下↑↓←→方向键控制小海龟移动,闯关成功后,按Enter进入下一关
:return:
"""
manual_t.clear() # 清除绘图
auto_t.clear() # 清除绘图
turtle.clear() # 清除绘图
global txt_path, level, mazeList, R, C # 定义全局变量
if level == 1: # 第一关的地图文件和背景
txt_path = "map/map1.txt"
levelbg = 'image/level1.png'
elif level == 2: # 第二关的地图文件和背景
txt_path = "map/map2.txt"
levelbg = 'image/level2.png'
elif level == 3: # 第三关的地图文件和背景
txt_path = "map/map3.txt"
levelbg = 'image/level3.png'
else:
turtle.bye() # 退出程序
return
mazeList = get_map(txt_path) # 获取地图数据
R, C = len(mazeList), len(mazeList[0])
turtle.setup(width=C * cell_size + 50, height=R * cell_size + 100) # 根据地图调整窗口尺寸
turtle.bgpic(levelbg) # 设置背景图片
'''
# 如果想要手动绘制关卡数,可以使用下面的两行代码
cmoveto(turtle, (1 * cellsize - C * cellsize / 2, R * cellsize / 2+10))
turtle.write('关卡:'+str(int(level)), font=('宋体', 16, 'normal'))
'''
turtle.ht() # 隐藏海龟光标
draw_map(mazeList) # 绘制地图
四、效果图
1.运行代码
2.关卡一
这是按住了F1所以才出现路线图的哦!
3.关卡二
这是按住了F2所以是自己手动在闯关哈!
4.关卡三