pygame开发:马赛逻辑小游戏的代码实现
目录
- 一、游戏简介
- 二、核心代码解析
- 三、pygame开发流程
- 1、从创建窗口到棋盘绘制
- 2、点击方格改变颜色
- 2.1、点击事件
- 2.2、碰撞检测
- 2.3、方格变色
- 2.4、阵列转换
- 2.5、效果初见
- 3、显示提示信息
- 四、游戏演示视频
一、游戏简介 马赛逻辑,是一个类似数独和扫雷的逻辑小游戏,根据棋盘周围的数据提示点亮方格,因外形像马赛克而得名。在手机游戏中有多款 APP 可以体验该游戏,如 Peak、Nonogram、Crossme 等。但在 PC 端,笔者暂时还未发现复刻版,于是打算自己动手实现一番。
文章图片
【pygame开发:马赛逻辑小游戏的代码实现】马赛逻辑的基本玩法如下图所示,上侧横向的各组数字为:对每一列中存在的目标方格的标注,如
2
表示该列有 2 个连续的目标,1 2
表示该列有 1 个独立的目标 + 2 个连续的目标。左侧纵向的各组数据为对每一行的标注。通过上、左两侧的提示,将所有目标方格点亮即为通关。文章图片
二、核心代码解析 在正式开始游戏开发之前,我们可以先想想实现这个项目的关键点在哪。首先,方格有选中和未选中两种状态,那可以用 1 表示选中、0 表示未选中。要判断玩家点亮的方格是否正确,只需将方块矩阵映射成 01 矩阵,再与答案矩阵对比即可。如此一来,出题也很容易,随机生成一串 01 组合即可。
文章图片
而最重要的地方在于,如何生成提示数值?我们需要分别对每行每列进行遍历,找出单独的 1 和连续的 1。下面以棋盘的一行为例进行说明。
文章图片
首先,准备一个列表类型的变量
remind
用于储存多个提示数值,并准备一个位移标记 flag
用于记录当前是在答案阵列的哪一位进行判断,以及一个数值记录 num
,再将答案阵列 [0, 1, 1, 0, 1, 0, 0, 1]
传入计数器。当传入阵列长度大于 1 时有四种情况,分别是:①当前位 0 ,次位 1;②当前位 1,次位 0;③连续多位 1;④连续多位 0。根据不同情况进行位移,将新的阵列传入计数器,并在 1 换 0 的时候记录数值。
当传入阵列等于 1 时有两种情况,分别是:①上位 0;②上位 1。根据不同的情况记录数值。
文章图片
按照这个思路,我们可以用一个简单的递归来实现这个提示算法,代码如下:
def get_line_remind(_line):# 输出一行或一列的提示remind = []# 一行或一列的提示记录num = 0# 提示值def fun(line):nonlocal remind, numflag = 0# 位移if len(line) > 1:if line[0] == 0 and line[1] == 1:flag += 1elif line[0] == line[1] == 0:flag += 2elif line[0] == 1 and line[1] == 0:num += 1remind.append(num)num = 0flag += 2elif line[0] == line[1] == 1:num += 1flag += 1fun(line[flag:])elif len(line) and line[0]:if num:remind.append(num + 1)else:remind.append(1)fun(_line)return remind
三、pygame开发流程
1、从创建窗口到棋盘绘制
棋盘的设计及玩法已经初具雏形了,可以正式开始制作游戏了啦!~笔者采用了有超过 20 年历史的游戏制作库 pygame,该游戏库包含了用于制作简单 2D 游戏的基本套件,python 及游戏爱好者们已经用它制作了成千上万的小游戏,使用 pip 安装即可使用。
第一步,对各类游戏元素的颜色、位置、尺寸等必要参数做一些设置。接着,初始化 pygame,绘制一个指定大小的窗口,使用
pygame.font.Font()
加载指定的字体文件,以防游戏打包后运行出错。需要注意的是,pygame 的所有视觉元素都建立在不断地重新绘制上,利用
pygame.display.flip()
进行整体更新。因为后期需要在白色背景中添加动态元素,所以将背景绘制放入主循环的首位。在主循环中,通过遍历事件来获取玩家的操作,当前仅追踪了一个退出事件。
import pygameimport sys# 参数设置 ----------------------------------blue = (159, 197, 232)# 被选中方格的颜色gray = (217, 217, 217)# 棋盘网格线颜色gold = (255, 215, 0)# 游戏记录文字颜色black = (0, 0, 0)white = (255, 255, 255)start_x = 240# 棋盘左上角位置start_y = 150size = 2# 一行/列的方块个数square = 320# 棋盘边长length = int(square / size)# 每个方块的边长# 游戏初始化 ----------------------------------pygame.init()screen = pygame.display.set_mode((780, 520))# 创建窗口font = pygame.font.Font(r'./data/msyh.ttf', 20)# 提示字体# 主循环 ----------------------------------while True:screen.fill(white)# 背景填充for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()# 退出pygamesys.exit()# 安全退出系统pygame.display.flip()# 更新全部显示
运行以上代码可以得到一片空白(雾)。
文章图片
下一步,我们来想想怎么绘制棋盘。首先,棋盘本身的尺寸是固定的,我们只需修改棋盘中的方格数量和大小,来改变棋局。因此,在第一步的参数设置中,使用
start_x
、start_y
来确定棋盘的位置,并设置棋盘的边长 square = 320
,以及一行中方块的个数 size
和方块边长 length
。因为方块是可以被点击而改变颜色的,所以我们要先自定义一个方块类。机制比较简单,初始化即传入坐标和边长,调用
pygame.draw.rect()
来绘制矩形。class Item:# 自定义方块类def __init__(self, pos_x, pos_y, leng):self.rect = pygame.draw.rect(screen, gray, [pos_x, pos_y, leng, leng], 0)self.state = False
再定义一个绘制棋盘的方法,从棋盘左上角开始,横竖各画
size
个方块,返回方块对象列表。def create_chessboard():# 创建棋盘item_lst = []for v in range(size):for h in range(size):rect = Item(start_x + h*length, start_y + v*length, length)item_lst.append(rect)return item_lst
由于方块初始颜色和背景色一样是白色,还需要加上网格线,横竖各画
size+1
条线,调用 pygame.draw.line()
绘制线条。def draw_line():# 绘制网格线for n in range(size+1):start = (start_x, start_y + n * length)end = (start_x + square, start_y + n * length)pygame.draw.line(screen, gray, start, end, 2)for n in range(size+1):start = (start_x + n * length, start_y)end = (start_x + n * length, start_y + square)pygame.draw.line(screen, gray, start, end, 2)
将主循环代码修改如下,注意:网格线是绘制在整个图层组的最上层,才不会被方格和背景覆盖掉。运行即可绘制出初始棋盘,如图为 4X4 的规格。
## 前文参数、初始化略......items = create_chessboard()# 创建棋盘while True:screen.fill(white)# 背景填充for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()# 退出pygamesys.exit()# 安全退出系统draw_line()# 绘制棋盘网格线pygame.display.flip()# 更新全部显示
文章图片
2、点击方格改变颜色
2.1、点击事件 在事件遍历中添加对鼠标点击事件的追踪,并获取点击坐标,之后通过判断点击的位置是否在某个方格中,即可得知是哪个方格被点击了,并作出颜色修改。
if event.type == pygame.MOUSEBUTTONDOWN:# 鼠标点击事件x, y = event.pos
2.2、碰撞检测 那么来写一个判断方法,将之前创建棋盘时得到的方块对象列表,和鼠标坐标传入,遍历方块并通过矩形的
collidepoint()
方法进行碰撞检测,若鼠标碰撞到了矩形区域,就对方块的状态取反。def check_click(item_lst, pos_x, pos_y):# 更新每个方块的点击状态for i in item_lst:if i.rect.collidepoint(pos_x, pos_y):i.state = bool(1 - i.state)
2.3、方格变色
def change_color(item_lst):# 根据状态改变方块颜色for i in item_lst:if i.state:pygame.draw.rect(screen, blue, i.rect, 0)else:pygame.draw.rect(screen, white, i.rect, 0)
2.4、阵列转换 再来写一个获取玩家操作阵列的方法,利用列表生成式将方块状态转换为 01 列表。
def create_answer_array():# 创建答案矩阵lst = [1 if random() > 0.5 else 0 for _ in range(size*size)]if list(set(lst))[0] == 0:lst[0] = 1return lst
并通过随机生成的方式来创建答案,之后通过比较两个列表即可判断游戏是否通关。别忘了,答案阵列中不能全都是 0。
def create_answer_array():# 创建答案矩阵lst = [1 if random() > 0.5 else 0 for _ in range(size*size)]if list(set(lst))[0] == 0:lst[0] = 1return lst
2.5、效果初见 修改主循环代码如下,运行后尝试点击可见效果。
## 前文参数、初始化略......answer = create_answer_array()# 创建答案矩阵# 主循环 ----------------------------------while True:screen.fill(white)# 背景填充for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()# 退出pygamesys.exit()# 安全退出系统if event.type == pygame.MOUSEBUTTONDOWN:# 鼠标点击事件x, y = event.poscheck_click(items, x, y)# 检查选中的方格,修改状态result = get_player_array(items)# 获取方格操作矩阵print(result)if result == answer:print("YOU WIN!")change_color(items)# 根据方格状态修改颜色draw_line()# 绘制棋盘网格线pygame.display.flip()# 更新全部显示
文章图片
3、显示提示信息
没有提示只能盲点怎么玩呀!赶紧把提示信息搞出来 (ε(#)☆╰╮o(皿///)
前文我们已经了解了提示算法,接下来就根据答案矩阵来整理两侧的提示信息。
def get_w_remind(answer_lst):# 根据答案矩阵输出提示列表h_remind = []v_remind = []h_array = [answer_lst[i: i+size] for i in range(0, len(answer_lst), size)]# 横向矩阵for h in h_array:h_remind.append(get_line_remind(h))v_array = list(map(list, zip(*h_array)))# 纵向矩阵for v in v_array:v_remind.append(get_line_remind(v))return h_remind, v_remind
由于笔者是直接使用一维列表来代替矩阵的,因此如果要获取每一行的提示,则需要按照
size
将将答案阵列分割成多份。而要获取每一列的提示时,则需要对分割好的横向矩阵进行行列转置。文章图片
之后,通过亿点点数学计算得到两侧信息的显示坐标,利用窗口对象的
blit()
方法将渲染好的文本对象贴上去。对横/纵阵列逆序的目的是,将多个提示数值从外到内显示,以符合阅读习惯。def show_remind(answer_lst):# 在棋盘两侧对应位置显示每行/列的提示h_remind, v_remind = get_w_remind(answer_lst)for i, h in enumerate(h_remind):for j, num in enumerate(h[::-1]):text = font.render(f"{num}", True, black)screen.blit(text, (start_x - 20 * (j + 1), start_y + i * length + length / 2 - 10))for i, v in enumerate(v_remind):for j, num in enumerate(v[::-1]):text = font.render(f"{num}", True, black)screen.blit(text, (start_x + i * length + length / 2 - 5, start_y - 30 * (j + 1)))
终于可以玩啦,来瞅瞅。
文章图片
至此,马赛逻辑的核心玩法已经实现,之后再完善一下游戏机制和体验效果,例如:修改难度、添加音效等,就可以打造一个相对完备的小游戏啦!(为了偷懒 )限于篇幅,在此不作赘述,可通过文末的方式获取带注释的最终版源码参考学习。
四、游戏演示视频 最终打包的游戏演示见下方视频,完整源码及打包后的游戏文件可通过以下方式获取(exe 文件可能会被误杀,需添加信任),感谢各位的阅读。
源码及游戏戳:http://xiazai.jb51.net/202109/yuanma/mosailogic_jb51.rar
以上就是pygame开发:马赛逻辑小游戏的代码实现的详细内容,更多关于pygame开发:马赛逻辑小游戏的资料请关注脚本之家其它相关文章!
推荐阅读
- 深入理解Go之generate
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- 我的软件测试开发工程师书单
- echart|echart 双轴图开发
- NPDP拆书(三)(新产品开发战略(经营与创新战略))
- 芯灵思SinlinxA33开发板Linux内核定时器编程
- 常用git命令总结
- 藏族开发的修路人——记致富援乡的斯定那珠
- ASP.NET|ASP.NET Core应用开发思维导图
- VueX(Vuex|VueX(Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式)