Python 实现消消乐小游戏
简介
消消乐(Match-3)是一款经典的益智游戏,核心玩法是通过交换相邻的方块,使三个或更多相同颜色的方块连成一线从而消除得分。本文使用 Python 和 Pygame 库实现一个基础的消消乐游戏,涵盖游戏界面绘制、网格管理、方块交换、匹配检测及消除逻辑。
本文介绍了使用 Python 和 Pygame 库开发消消乐小游戏的完整过程。内容涵盖环境搭建、游戏窗口初始化、8x8 网格绘制、随机拼图块生成、鼠标交互逻辑、相邻方块交换机制、匹配检测算法以及消除后的方块下落与补充逻辑。文章提供了详细的代码示例,解释了计分器、计时器及游戏结束界面的实现方式,适合希望学习游戏开发基础逻辑的开发者参考。

消消乐(Match-3)是一款经典的益智游戏,核心玩法是通过交换相邻的方块,使三个或更多相同颜色的方块连成一线从而消除得分。本文使用 Python 和 Pygame 库实现一个基础的消消乐游戏,涵盖游戏界面绘制、网格管理、方块交换、匹配检测及消除逻辑。
在开始之前,请确保已安装 Python 环境并安装 Pygame 库:
pip install pygame
游戏主体主要由窗口设置、网格系统、计分器和计时器组成。首先定义全局常量和必要的导入模块。
import os
import sys
import time
import pygame
import random
设定窗口大小、网格行列数及边距等参数,便于后续调整。
WIDTH = 400
HEIGHT = 400
NUMGRID = 8
GRIDSIZE = 36
XMARGIN = (WIDTH - GRIDSIZE * NUMGRID) // 2
YMARGIN = (HEIGHT - GRIDSIZE * NUMGRID) // 2
ROOTDIR = os.getcwd()
FPS = 30
使用 Pygame 初始化显示模式并设置窗口标题。
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('消消乐')
在主循环中填充背景色,并调用绘制函数画出 8x8 的网格线。
screen.fill((255, 255, 220))
def drawGrids(self):
for x in range(NUMGRID):
for y in range(NUMGRID):
rect = pygame.Rect(
XMARGIN + x * GRIDSIZE,
YMARGIN + y * GRIDSIZE,
GRIDSIZE, GRIDSIZE
)
self.drawBlock(rect, color=(255, 165, 0), size=1)
def drawBlock(self, block, color=(255, 0, 0), size=2):
pygame.draw.rect(self.screen, color, block, size)
初始化时,在网格中随机放置不同颜色的拼图块(Gems)。为了避免初始状态直接存在可消除的组合,需进行预检查。
while True:
self.all_gems = []
self.gems_group = pygame.sprite.Group()
for x in range(NUMGRID):
self.all_gems.append([])
for y in range(NUMGRID):
gem = Puzzle(
img_path=random.choice(self.gem_imgs),
size=(GRIDSIZE, GRIDSIZE),
position=[XMARGIN + x * GRIDSIZE, YMARGIN + y * GRIDSIZE - NUMGRID * GRIDSIZE],
downlen=NUMGRID * GRIDSIZE
)
self.all_gems[x].append(gem)
self.gems_group.add(gem)
if self.isMatch()[0] == 0:
break
游戏需要实时显示当前分数、加分动画以及剩余时间。
def drawScore(self):
score_render = self.font.render('分数:' + str(self.score), 1, (85, 65, 0))
rect = score_render.get_rect()
rect.left, rect.top = (55, 15)
self.screen.blit(score_render, rect)
当发生消除时,弹出加分提示。
def drawAddScore(self, add_score):
score_render = self.font.render('+' + str(add_score), 1, (255, 100, 100))
rect = score_render.get_rect()
rect.left, rect.top = (250, 250)
self.screen.blit(score_render, rect)
def showRemainingTime(self):
remaining_time_render = self.font.render('倒计时:%ss' % str(self.remaining_time), 1, (85, 65, 0))
rect = remaining_time_render.get_rect()
rect.left, rect.top = (WIDTH - 190, 15)
self.screen.blit(remaining_time_render, rect)
当时间耗尽,显示最终得分并提供重新开始选项。
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYUP and event.key == pygame.K_r:
flag = True
if flag:
break
screen.fill((255, 255, 220))
text0 = '最终得分:%s' % score
text1 = '按 R 键重新开始'
y = 140
for idx, text in enumerate([text0, text1]):
text_render = font.render(text, 1, (85, 65, 0))
rect = text_render.get_rect()
if idx == 0:
rect.left, rect.top = (100, y)
elif idx == 1:
rect.left, rect.top = (100, y)
y += 60
screen.blit(text_render, rect)
pygame.display.update()
通过鼠标位置判断是否点击了某个拼图块。
def checkSelected(self, position):
for x in range(NUMGRID):
for y in range(NUMGRID):
if self.getGemByPos(x, y).rect.collidepoint(*position):
return [x, y]
return None
允许玩家交换相邻的两个方块。代码需验证交换位置是否相邻(距离为 1),并更新方向和目标坐标。
def swapGem(self, gem1_pos, gem2_pos):
margin = gem1_pos[0] - gem2_pos[0] + gem1_pos[1] - gem2_pos[1]
if abs(margin) != 1:
return False
gem1 = self.getGemByPos(*gem1_pos)
gem2 = self.getGemByPos(*gem2_pos)
# 确定移动方向
if gem1_pos[0] - gem2_pos[0] == 1:
gem1.direction = 'left'
gem2.direction = 'right'
elif gem1_pos[0] - gem2_pos[0] == -1:
gem2.direction = 'left'
gem1.direction = 'right'
elif gem1_pos[1] - gem2_pos[1] == 1:
gem1.direction = 'up'
gem2.direction = 'down'
elif gem1_pos[1] - gem2_pos[1] == -1:
gem2.direction = 'up'
gem1.direction = 'down'
# 设置目标位置
gem1.target_x = gem2.rect.left
gem1.target_y = gem2.rect.top
gem1.fixed = False
gem2.target_x = gem1.rect.left
gem2.target_y = gem1.rect.top
gem2.fixed = False
# 交换数组引用
self.all_gems[gem2_pos[0]][gem2_pos[1]] = gem1
self.all_gems[gem1_pos[0]][gem1_pos[1]] = gem2
return True
遍历网格,检查是否存在横向或纵向连续三个及以上相同类型的方块。
def isMatch(self):
for x in range(NUMGRID):
for y in range(NUMGRID):
# 检查横向
if x + 2 < NUMGRID:
if (self.getGemByPos(x, y).type == self.getGemByPos(x+1, y).type ==
self.getGemByPos(x+2, y).type):
return [1, x, y]
# 检查纵向
if y + 2 < NUMGRID:
if (self.getGemByPos(x, y).type == self.getGemByPos(x, y+1).type ==
self.getGemByPos(x, y+2).type):
return [2, x, y]
return [0, x, y]
当检测到匹配时,移除对应方块,上方方块下落,顶部生成新方块。
def removeMatched(self, res_match):
if res_match[0] > 0:
self.generateNewGems(res_match)
self.score += self.reward
return self.reward
return 0
def generateNewGems(self, res_match):
if res_match[0] == 1: # 横向消除
start = res_match[2]
while start > -2:
for each in [res_match[1], res_match[1]+1, res_match[1]+2]:
gem = self.getGemByPos(*[each, start])
if start == res_match[2]:
self.gems_group.remove(gem)
self.all_gems[each][start] = None
elif start >= 0:
gem.target_y += GRIDSIZE
gem.fixed = False
gem.direction = 'down'
self.all_gems[each][start+1] = gem
else:
gem = Puzzle(
img_path=random.choice(self.gem_imgs),
size=(GRIDSIZE, GRIDSIZE),
position=[XMARGIN + each * GRIDSIZE, YMARGIN - GRIDSIZE],
downlen=GRIDSIZE
)
self.gems_group.add(gem)
self.all_gems[each][start+1] = gem
start -= 1
elif res_match[0] == 2: # 纵向消除
start = res_match[2]
while start > -4:
if start == res_match[2]:
for each in range(0, 3):
gem = self.getGemByPos(*[res_match[1], start+each])
self.gems_group.remove(gem)
self.all_gems[res_match[1]][start+each] = None
elif start >= 0:
gem = self.getGemByPos(*[res_match[1], start])
gem.target_y += GRIDSIZE * 3
gem.fixed = False
gem.direction = 'down'
self.all_gems[res_match[1]][start+3] = gem
else:
gem = Puzzle(
img_path=random.choice(self.gem_imgs),
size=(GRIDSIZE, GRIDSIZE),
position=[XMARGIN + res_match[1] * GRIDSIZE, YMARGIN + start * GRIDSIZE],
downlen=GRIDSIZE * 3
)
self.gems_group.add(gem)
self.all_gems[res_match[1]][start+3] = gem
start -= 1
本文详细讲解了使用 Python 和 Pygame 构建消消乐游戏的核心流程,包括界面渲染、事件监听、数据交换及消除算法。通过模块化设计,可以进一步扩展关卡系统、道具功能或音效支持。开发者可根据实际需求调整网格大小、颜色种类及游戏规则。
注:以上代码片段展示了关键逻辑,实际运行需结合完整的类结构定义及资源文件加载。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online