Merlin's Blog
Just record something
Toggle navigation
Merlin's Blog
Home
Scratch基础教程
About Me
Archives
Tags
pgzero游戏:拼图
2023-02-19 18:50:57
57
0
0
merlin
##**一、添加图块** ###**1.创建游戏场景** 图片采用96×96大小,界面划分成3×3的九宫格。 ``` import pgzrun import random SIZE = 96 #图块边长 WIDTH = 3 * SIZE #窗口宽度 HEIGHT = 3 * SIZE #窗口高度 finish = False #布尔变量,用于判断游戏是否结束 pics = [] #角色列表 ``` 绘制背景,这里用白色背景。 ``` def draw(): screen.fill((255,255,255)) pgzrun.go() ``` 运行测试,就能看到一个288*288的白色窗口。 ###**2.列表管理图片块** 对这个游戏而言,每个图块都可以操作,所以都应看成角色。为了方便处理,用列表来储存角色(回忆消消乐)。 角色创建用Actor()方法。 在pic = [] 语句之后添加代码: ``` for i in range(8): pic = Actor(f"puzzle_pic{i}") pic.index = i pics.append(pic) ``` 因为图片统一命名格式为:puzzle_pic+编号.png 所以用循环可以快速创建角色,此方法在消消乐中也用过了。 9张图片却创建了8个角色?玩过华容道的知道,需要留出一个空白位置,否则图片就无法移动了,所以9个图块,只创建了8个角色。 index属性的编号,到时候要用于检测拼图是否完成。 ##**二、打乱图块** ###**1.使用打乱列表方法** 我们已经学过random模块的随机整数方法,现在来学习一下打乱列表的方法。 ``` random.shuffle(列表名) ``` shuffle()方法能够打乱列表中的顺序,这样每次出现都是打乱的图块。 在角色列表生成代码之后,加入以下语句,注意跟上面的for对齐: ``` random.shuffle(pics) ``` ###**2.将图片显示出来** 回顾pgzrun的坐标知识和角色位置设置。  在学习消消乐的时候讲解过角色位置定位的方法,角色中心可以用x和y来设置,但是要平铺,应该使用(left,top)或(right,bottom) 那么九宫格是3×3的表格,可以表示为(图块左上角坐标):  可以把编号对3进行求余或商,计算出这些位置。 **列是编号对3求余** **行是编号对3求商** 看下图规律:  对一组连续的数而言,都去除以同一个整数,余数规律必然是0、1、... 同理,都去除以同一个整数,商的规律每隔整数个就会加1。 这样求出来的坐标,刚好填满九格宫。 因为图块边长为SIZE,我们设置位置时,需要知道像素的位置,所以需要把**行和列都去×SIZE**,计算出实际位置,在**打乱pics列表后**再计算角色的实际位置。 ```python for i in range(8): pics[i].left = i%3*SIZE #角色实际列的位置 pics[i].top = i//3*SIZE #角色实际行的位置 ``` 接着修改draw()函数,把图块画出来。 ``` def draw(): screen.fill((255,255,255)) for pic in pics: pic.draw() ``` 运行测试,将会看到打乱的图块。 ##**三、移动图块** ###**1.处理鼠标单击事件** 在消消乐已经接触过鼠标事件,复习一下用法: ``` def on_mouse_down(pos): print(f"x={pos[0]}") print(f"y={pos[1]}") ``` 运行测试。 ###**2.获取图块在九宫格中的位置** 鼠标点击获得的坐标是以像素为单位的,我们需要转换为九宫格的坐标。 之前**九宫格坐标->像素坐标**,我们把行和列都乘SIZE,所以还原反过来计算就可以了,即$pos[0]//SIZE$和$pos[1]//SIZE$。 游戏逻辑: (1).点击图块(也有可能点空白处),所以要判断当前是不是真的图块。 (2).上下左右判断,根据坐标寻找空白位置,找到空白处,把当前点中的图块移动。上下移动,角色y属性加减SIZE;左右移动,角色x属性加减SIZE。 总和1和2,我们都需要判断某个位置是图块还是空白,所以需要自行设计一个函数来实现。 ```python def is_pic(x,y): for pic in pics: #遍历所有图块角色 if pic.x//SIZE == x and pic.y//SIZE == y: #是否找到图块 return pic #返回角色 return None #None,空值,表示没有 ``` 修改鼠标单击事件代码如下: ```python def on_mouse_down(pos): pic_x = pos[0]//SIZE #计算九宫格中的列 pic_y = pos[1]//SIZE #计算九宫格中的行 now_pic = is_pic(pic_x,pic_y) #判断是否是图块 if now_pic == None: #不是图块 return ``` ###**3.判断图块是否能移动** 根据前面讲的,当前点击的如果是图块,就上下左右寻找空白处。  所以,我们只要判断这4个位置是否为None,如果为None,则当前图块往空白处移动. ``` if pic_y>0 and is_pic(pic_x,pic_y-1) == None: now_pic.y -= SIZE ``` 解释:pic_x,pic_y 是当前鼠标点击的位置(已转换为数组中的下标),所以鼠标点击图片的上一张图片位置为(pic_x,pic_y-1),先要判断是否**越界**,然后用is_pic()函数判断是不是空白,即返回结果是否是**None**,如果是None,则当前鼠标单击的图块向上移动SIZE个像素(数组坐标中上下相差1)。 **挑战** 剩下3个方向该如何写?(对照上图和向上移的代码思考) ```python def on_mouse_down(pos): pic_x = pos[0]//SIZE #计算当前点击在二维数组中的位置 pic_y = pos[1]//SIZE now_pic = is_pic(pic_x,pic_y) #判断是空白还是图块 if now_pic == None: #如果是图块,则不处理 return if pic_y>0 and is_pic(pic_x,pic_y-1) == None: now_pic.y -= SIZE if ... ... if ... ... if ... ... ``` ##**四、游戏结束设计** ###**1.检查拼图是否完成** 在创建角色的时候,我们依次给角色的index属性编了号,所以要判断拼图是否完成,就要看图块有没有按index属性从小到大排列,即下图:  所以,我们的思路是:从左往右,从上往下,查询图块的index属性,回想之前如何给图块计算实际像素的位置?  ```python for i in range(8): pics[i].left = i%3*SIZE #角色实际列的位置 pics[i].top = i//3*SIZE #角色实际行的位置 ``` 所以,把数组中的位置遍历,只要不去乘SIZE即可。 代码如下: ```python def update(): for i in range(8): pic = is_pic(i%3,i//3) #查询二维的所有坐标 if(pic == None or pic.index!=i): #返回的不是图块或编号对不上,则拼图还没完成 return ``` **注意:update()跟draw()一样,也是属于pgzero框架函数,作用为隔一段时间执行一次update()** 虽然判断图块是否移正确已经完成,但是程序还不知道已拼完。 之前我们设了一个**布尔变量**:finish。它初值为**False**,表示拼图还没完成,所以,如果检测全部通过,程序应该修改它的值为True. 修改update()如下: ```python def update(): global finish #全局变量,加global声明 if finish==True: #拼图已经结束,则不需要判断 return for i in range(8): pic = is_pic(i%3,i//3) if(pic == None or pic.index!=i): return finish = True #全部通过,标记为完成 ``` 然后,应该在每次移动之后,调用这个函数判断拼图是否已经完成。所以在鼠标点击事件的最后,调用update()。 ###**2.显示最后一张图片** 虽然拼图完成的逻辑已完成,实际去点击的时候,图块还能移动。所以,我们要把最后一块图片补上去,即右下角。 在批量创建角色之后,单独创建一个图块角色,然后当拼图完成的时候,把它画出来。 ``` lastpic = Actor("puzzle_pic8") lastpic.left = 2 * SIZE lastpic.top = 2 * SIZE ``` 在draw()函数中,判断拼图是否完成,如完成,则画出最后一张图块,为了保证图片不再移动,把最后一张图片添加到角色列表中,这样draw()会自动补上最后一块,所以update()方法中加上列表添加语句即可。 ```python def update(): global finish #全局变量,加global声明 if finish==True: #拼图已经结束,则不需要判断 return for i in range(8): pic = is_pic(i%3,i//3) if(pic == None or pic.index!=i): return finish = True #全部通过,标记为完成 pics.append(lastpic) #添加最后的图片 ``` ###**3.播放游戏完成的声音** 这个以前学过了,建一个sounds文件夹,放入声音文件(wav或ogg),然后函数调用即可。 为了只播放一次,应该放在update()方法内,修改如下: ```python def update(): global finish #全局变量,加global声明 if finish==True: #拼图已经结束,则不需要判断 return for i in range(8): pic = is_pic(i%3,i//3) if(pic == None or pic.index!=i): return finish = True #全部通过,标记为完成 pics.append(lastpic) #添加最后的图片 sounds.win.play() ``` ###**4.加上完成字幕** 用screen.draw.text()方法可以在屏幕上输出文本(中文不行)。 绘制和输出信息,要在draw()方法中进行,代码修改如下: ```python def draw(): screen.fill((255,255,255)) for pic in pics: pic.draw() if finish: screen.draw.text("Congratulation!",center=(WIDTH//2,HEIGHT//2),fontsize=50,color="red") ```
Pre:
pgzero游戏:贪吃蛇
Next:
对拍程序写法(c++版)
0
likes
57
Weibo
Wechat
Tencent Weibo
QQ Zone
RenRen
Table of content