专栏首页数据STUDIO全网首发!你没玩过的pygame小游戏开发「马赛逻辑」

全网首发!你没玩过的pygame小游戏开发「马赛逻辑」

大家好,我是云朵君!

Python除了不能帮你生孩子,还真无所不能!今天给大家带来一个很有意思的python小游戏开发,文末提供源码,一起学习呀~

作者简介:Seon塞翁,数据分析从业者,专注用python处理数据、调教excel、开发有趣实用的GUI小玩意儿,乐于分享python学习之路上的经验技巧。

本号长期接受优质文章投稿,详情请移步至公号菜单栏【云朵之家】-【投稿】,稿酬丰厚,欢迎来稿!

👆点击关注|设为星标|干货速递👆


游戏简介

马赛逻辑,是一个类似数独和扫雷的逻辑小游戏,根据棋盘周围的数据提示点亮方格,因外形像马赛克而得名。在手机游戏中有多款 APP 可以体验该游戏,如 Peak、Nonogram、Crossme 等。但在 PC 端,笔者暂时还未发现复刻版,于是打算自己动手实现一番。

手游app

马赛逻辑的基本玩法如下图所示,上侧横向的各组数字为:对每一列中存在的目标方格的标注,如 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, num
        flag = 0  # 位移
        if len(line) > 1:
            if line[0] == 0 and line[1] == 1:
                flag += 1
            elif line[0] == line[1] == 0:
                flag += 2
            elif line[0] == 1 and line[1] == 0:
                num += 1
                remind.append(num)
                num = 0
                flag += 2
            elif line[0] == line[1] == 1:
                num += 1
                flag += 1
            fun(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 pygame
import 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 = 150
size = 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()  # 退出pygame
            sys.exit()  # 安全退出系统
    pygame.display.flip()  # 更新全部显示

运行以上代码可以得到一片空白(雾)。

代码运行结果

下一步,我们来想想怎么绘制棋盘。首先,棋盘本身的尺寸是固定的,我们只需修改棋盘中的方格数量和大小,来改变棋局。因此,在第一步的参数设置中,使用 start_xstart_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()  # 退出pygame
            sys.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 get_player_array(item_lst):  # 获取玩家操作矩阵
    return [1 if i.state else 0 for i in item_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] = 1
    return 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()  # 退出pygame
            sys.exit()  # 安全退出系统
        if event.type == pygame.MOUSEBUTTONDOWN:  # 鼠标点击事件
            x, y = event.pos
            check_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 文件可能会被误杀,需添加信任),感谢各位的阅读。

演示视频地址: https://player.bilibili.com/player.html?aid=890301858 源码获取: 请在文末点赞和在看后,扫描下方二维码,添加云朵君微信好友,备注【马赛逻辑】,免费获取!

本文分享自微信公众号 - 数据STUDIO(jim_learning),作者:Seon塞翁

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-09-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 我们整理了20个Python项目,送给正在求职的你

    职场中一贯有“金三银四”、“金九银十”的说法。如果你是一名正在求职或准备跳槽的程序员,不妨趁着这两个月时间好好准备一下。

    用户7886150
  • Python制作十款经典的童年游戏(附源码)

    不知道行友们每年六一是怎么度过的,行哥的童年是在游戏世界里度过。这里行哥分享一下十个python可以制作的经典游戏,看看有没有你的菜,代码链接放在文末

    行哥玩Python
  • 从零开始学习PYTHON3讲义(十四)写一个mp3播放器

    通常来说,Python解释执行,运行速度慢,并不适合完整的开发游戏。随着电脑速度的快速提高,这种情况有所好转,但开发游戏仍然不是Python的重点工作。 大多...

    俺踏月色而来
  • 【人工智障入门实战1】无需公式或代码,用生活实例谈谈强化学习算法框架

    战胜围棋高手李世石的 AlphaGo ,称霸星际争霸2的 AIphaStar...这些先进的自动控制技术都离不开“强化学习”这个算法框架。有人说,强化学习是一种...

    Piper蛋窝
  • 一步步教你怎么用python写贪吃蛇游戏

    前几天,星球有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Python(蛇)玩Snake(贪吃蛇),那...

    诸葛青云
  • 用Python 优雅的打飞机

    相信很多朋友都用java 写过飞机大战,在自己学完python基础以后就开始写python版飞机大战,今天把用pygame实现飞机大战的游戏分享给大家。

    PM小王
  • 你的游戏开发第0课

    电子游戏是许多人喜爱甚至沉迷的事情。尤其对于程序员来说,开发游戏是不少人最初学习编程的动力。在之前,我发过一些游戏开发的教程和案例:

    Crossin先生
  • python---图形界面的2048

    之前做了控制台版的2048,那样子玩着不爽,玩着没那么随意,于是把他做成带ui的2048。大部分的逻辑都是一样的,做这种小游戏,最难的就是的逻辑实现过程,一旦...

    sjw1998
  • 一步步教你怎么用python写贪吃蛇游戏

    前几天,星球有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Python(蛇)玩Snake(贪吃蛇),那...

    Python进击者
  • 不就是小游戏嘛,分分钟给你写一个

    前天有同学无意间把一个小游戏分享到了答疑群中,我看了一下,其实游戏的代码逻辑并不复杂(简化版的跳一跳,套上个吃鸡的主题),于是就随手立了一个FLAG:

    Crossin先生
  • 自制街机游戏(2):再次实现

    在本节中,我不演示如何逐步设计和实现游戏,而在源代码中包含大量的注释和文档字符串。你可通过研究源代码来了解其工作原理,但这里还是简单地说说其中的要点(以及不那么...

    不可言诉的深渊
  • 手把手教你用python写游戏

    最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏、后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大...

    用户7886150
  • 手把手教你使用 Python 制作贪吃蛇游戏

    贪吃蛇游戏是有史以来最受欢迎的街机游戏之一。在这个游戏中,玩家的主要目标是在不撞墙或不撞墙的情况下抓住最大数量的水果。在学习 Python 或 Pygame 时...

    海拥
  • python应用篇之外星人入侵项目——武装飞船(中)

      从上篇文章,我们已经开始进入项目应用阶段,上篇文章给大家介绍了《外星人入侵》的基本玩法以及环境的安装,最后将游戏的背景设置出来;本文继续给大家介绍武装飞船的...

    一计之长
  • 用 Python 实现打飞机,让子弹飞吧!

    安装好 pygame 在第一次使用 pygame 的时候,pyCharm 会自动 install pygame。

    猫咪编程
  • 7.10 VR扫描:VR恐怖游戏《Gates of Nowhere》已提前登陆Steam

    微软系MR头显是微软和一众OEM大厂合作开发的,其即插即用性相比其HTC Vive等更加的平易近人。近日,亚马逊上大多数Windows MR头显正在大折扣促销中...

    VRPinea
  • 从零开始学习PYTHON3讲义(十五)让画面动起来

    虽然看起来绘图和音乐并不相关,但是听过了上一讲的内容你一定知道,这是游戏编程中四个需要处理内容的两部分,这两部分必须同时、并行的处理,不能因为某一项计算的拖延,...

    俺踏月色而来
  • 【参赛经验分享】分析js代码开启游玩新世界与Pierre Dellacherie算法本地验证

    以下仅是我对于这个比赛的思考过程,可能是拿高分的技巧,但我并没有因此拿高分,本人算法水平有限大佬勿喷,对文章中的问题欢迎指出。

    一颗小树
  • 手游后台PVP系统网络同步方案总结

    来源:游迅网 发布者:wuyu 概述   PVP系统俨然成为现在新手游的上线标配,手游Pvp系统体验是否优秀,很大程度上决定了游戏的品质。从最近半年上线的新...

    编程范 源代码公司

扫码关注云+社区

领取腾讯云代金券