Python游戏开发,pygame模块,Python实现扫雷小游戏

Cooci 2021-08-09 13:29:35
游戏开发 pygame Python3.x 小游戏 模块依赖


前言

今天给大家分享是扫雷小游戏,废话不多说,让我们愉快地开始吧~

效果展示

在这里插入图片描述

开发工具

Python版本: 3.6.4

相关模块:

pygame模块;

以及一些python自带的模块。

环境搭建

安装Python并添加到环境变量,pip安装需要的相关模块即可。

原理简介

这这里我简单介绍一下游戏的实现思路。

相信大家对扫雷这款windows自带的经典小游戏都不陌生,它的游戏规则很简单:

扫雷

游戏界面左上角的数字代表所有方格中埋有雷的数目,右上角是一个计时器。你要做的就是根据提示找出方格中所有的雷。

那么提示是啥呢?就是游戏刚开始的时候你需要随便点一个方格,就像这样:
在这里插入图片描述

上面的数字代表以该数字为中心的九宫格内埋有的雷的数目。所以如果你人品不好,一开始就点到雷的话,这局游戏就直接结束了。

ok,了解了游戏规则之后,我们就可以开始写代码了。首先还是先初始化一下游戏:

# 游戏初始化
pygame.init()
screen = pygame.display.set_mode(cfg.SCREENSIZE)
pygame.display.set_caption('mine sweeper —— ilove-python')

然后把需要用到的字体,图片,音乐啥的都导入进来:

# 导入所有图片
images = {}
for key, value in cfg.IMAGE_PATHS.items():
if key in ['face_fail', 'face_normal', 'face_success']:
image = pygame.image.load(value)
images[key] = pygame.transform.smoothscale(image, (int(cfg.GRIDSIZE*1.25), int(cfg.GRIDSIZE*1.25)))
else:
image = pygame.image.load(value).convert()
images[key] = pygame.transform.smoothscale(image, (cfg.GRIDSIZE, cfg.GRIDSIZE))
# 载入字体
font = pygame.font.Font(cfg.FONT_PATH, cfg.FONT_SIZE)
# 导入并播放背景音乐
pygame.mixer.music.load(cfg.BGM_PATH)
pygame.mixer.music.play(-1)

接着,我们来定义一个文字板,用于显示左上角的埋雷数量和右上角的游戏已进行时间:

'''文字板'''
class TextBoard(pygame.sprite.Sprite):
def __init__(self, text, font, position, color, **kwargs):
pygame.sprite.Sprite.__init__(self)
self.text = text
self.font = font
self.position = position
self.color = color
def draw(self, screen):
text_render = self.font.render(self.text, True, self.color)
screen.blit(text_render, self.position)
def update(self, text):
self.text = text

其实很简单,只需要把用字体渲染之后的文本对象绑定到屏幕上就行,然后设置一个update函数,来实时更新里面的文本内容。

然后,我们再定义一个表情按钮类:
'''表情按钮'''
class EmojiButton(pygame.sprite.Sprite):
def __init__(self, images, position, status_code=0, **kwargs):
pygame.sprite.Sprite.__init__(self)
# 导入图片
self.images = images
self.image = self.images['face_normal']
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = position
# 表情按钮的当前状态
self.status_code = status_code
'''画到屏幕上'''
def draw(self, screen):
# 状态码为0, 代表正常的表情
if self.status_code == 0:
self.image = self.images['face_normal']
# 状态码为1, 代表失败的表情
elif self.status_code == 1:
self.image = self.images['face_fail']
# 状态码为2, 代表成功的表情
elif self.status_code == 2:
self.image = self.images['face_success']
# 绑定图片到屏幕
screen.blit(self.image, self.rect)
'''设置当前的按钮的状态'''
def setstatus(self, status_code):
self.status_code = status_code

当鼠标点击到这个按钮的时,就重新开始新的游戏(无论当前的游戏状态如何,都将重新开始新的游戏):

接下来,我们需要定义的就是下面的方格类了:

'''雷'''
class Mine(pygame.sprite.Sprite):
def __init__(self, images, position, status_code=0, **kwargs):
pygame.sprite.Sprite.__init__(self)
# 导入图片
self.images = images
self.image = self.images['blank']
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = position
# 雷当前的状态
self.status_code = status_code
# 真雷还是假雷(默认是假雷)
self.is_mine_flag = False
# 周围雷的数目
self.num_mines_around = -1
'''设置当前的状态码'''
def setstatus(self, status_code):
self.status_code = status_code
'''埋雷'''
def burymine(self):
self.is_mine_flag = True
'''设置周围雷的数目'''
def setnumminesaround(self, num_mines_around):
self.num_mines_around = num_mines_around
'''画到屏幕上'''
def draw(self, screen):
# 状态码为0, 代表该雷未被点击
if self.status_code == 0:
self.image = self.images['blank']
# 状态码为1, 代表该雷已被点开
elif self.status_code == 1:
self.image = self.images['mine'] if self.is_mine_flag else self.images[str(self.num_mines_around)]
# 状态码为2, 代表该雷被玩家标记为雷
elif self.status_code == 2:
self.image = self.images['flag']
# 状态码为3, 代表该雷被玩家标记为问号
elif self.status_code == 3:
self.image = self.images['ask']
# 状态码为4, 代表该雷正在被鼠标左右键双击
elif self.status_code == 4:
assert not self.is_mine_flag
self.image = self.images[str(self.num_mines_around)]
# 状态码为5, 代表该雷在被鼠标左右键双击的雷的周围
elif self.status_code == 5:
self.image = self.images['0']
# 状态码为6, 代表该雷被踩中
elif self.status_code == 6:
assert self.is_mine_flag
self.image = self.images['blood']
# 状态码为7, 代表该雷被误标
elif self.status_code == 7:
assert not self.is_mine_flag
self.image = self.images['error']
# 绑定图片到屏幕
screen.blit(self.image, self.rect)
@property
def opened(self):
return self.status_code == 1

它的主要作用就是记录游戏地图中某个方格的状态(比如是不是埋了雷呀,有没有被点开呀,有没有被标记呀之类的)。

最后定义一个游戏地图类,来把游戏地图中的所有方格都整合在一起方便在游戏主循环里调用更新:

'''扫雷地图'''
class MinesweeperMap():
def __init__(self, cfg, images, **kwargs):
self.cfg = cfg
# 雷型矩阵
self.mines_matrix = []
for j in range(cfg.GAME_MATRIX_SIZE[1]):
mines_line = []
for i in range(cfg.GAME_MATRIX_SIZE[0]):
position = i * cfg.GRIDSIZE + cfg.BORDERSIZE, (j + 2) * cfg.GRIDSIZE
mines_line.append(Mine(images=images, position=position))
self.mines_matrix.append(mines_line)
# 随机埋雷
for i in random.sample(range(cfg.GAME_MATRIX_SIZE[0]*cfg.GAME_MATRIX_SIZE[1]), cfg.NUM_MINES):
self.mines_matrix[i//cfg.GAME_MATRIX_SIZE[0]][i%cfg.GAME_MATRIX_SIZE[0]].burymine()
count = 0
for item in self.mines_matrix:
for i in item:
count += int(i.is_mine_flag)
# 游戏当前的状态
self.status_code = -1
# 记录鼠标按下时的位置和按的键
self.mouse_pos = None
self.mouse_pressed = None
'''画出当前的游戏状态图'''
def draw(self, screen):
for row in self.mines_matrix:
for item in row: item.draw(screen)
'''设置当前的游戏状态'''
def setstatus(self, status_code):
# 0: 正在进行游戏, 1: 游戏结束, -1: 游戏还没开始
self.status_code = status_code
'''根据玩家的鼠标操作情况更新当前的游戏状态地图'''
def update(self, mouse_pressed=None, mouse_pos=None, type_='down'):
assert type_ in ['down', 'up']
# 记录鼠标按下时的位置和按的键
if type_ == 'down' and mouse_pos is not None and mouse_pressed is not None:
self.mouse_pos = mouse_pos
self.mouse_pressed = mouse_pressed
# 鼠标点击的范围不在游戏地图内, 无响应
if self.mouse_pos[0] < self.cfg.BORDERSIZE or self.mouse_pos[0] > self.cfg.SCREENSIZE[0] - self.cfg.BORDERSIZE or \
self.mouse_pos[1] < self.cfg.GRIDSIZE * 2 or self.mouse_pos[1] > self.cfg.SCREENSIZE[1] - self.cfg.BORDERSIZE:
return
# 鼠标点击在游戏地图内, 代表开始游戏(即可以开始计时了)
if self.status_code == -1:
self.status_code = 0
# 如果不是正在游戏中, 按鼠标是没有用的
if self.status_code != 0:
return
# 鼠标位置转矩阵索引
coord_x = (self.mouse_pos[0] - self.cfg.BORDERSIZE) // self.cfg.GRIDSIZE
coord_y = self.mouse_pos[1] // self.cfg.GRIDSIZE - 2
mine_clicked = self.mines_matrix[coord_y][coord_x]
# 鼠标按下
if type_ == 'down':
# --鼠标左右键同时按下
if self.mouse_pressed[0] and self.mouse_pressed[2]:
if mine_clicked.opened and mine_clicked.num_mines_around > 0:
mine_clicked.setstatus(status_code=4)
num_flags = 0
coords_around = self.getaround(coord_y, coord_x)
for (j, i) in coords_around:
if self.mines_matrix[j][i].status_code == 2:
num_flags += 1
if num_flags == mine_clicked.num_mines_around:
for (j, i) in coords_around:
if self.mines_matrix[j][i].status_code == 0:
self.openmine(i, j)
else:
for (j, i) in coords_around:
if self.mines_matrix[j][i].status_code == 0:
self.mines_matrix[j][i].setstatus(status_code=5)
# 鼠标释放
else:
# --鼠标左键
if self.mouse_pressed[0] and not self.mouse_pressed[2]:
if not (mine_clicked.status_code == 2 or mine_clicked.status_code == 3):
if self.openmine(coord_x, coord_y):
self.setstatus(status_code=1)
# --鼠标右键
elif self.mouse_pressed[2] and not self.mouse_pressed[0]:
if mine_clicked.status_code == 0:
mine_clicked.setstatus(status_code=2)
elif mine_clicked.status_code == 2:
mine_clicked.setstatus(status_code=3)
elif mine_clicked.status_code == 3:
mine_clicked.setstatus(status_code=0)
# --鼠标左右键同时按下
elif self.mouse_pressed[0] and self.mouse_pressed[2]:
mine_clicked.setstatus(status_code=1)
coords_around = self.getaround(coord_y, coord_x)
for (j, i) in coords_around:
if self.mines_matrix[j][i].status_code == 5:
self.mines_matrix[j][i].setstatus(status_code=0)
'''打开雷'''
def openmine(self, x, y):
mine_clicked = self.mines_matrix[y][x]
if mine_clicked.is_mine_flag:
for row in self.mines_matrix:
for item in row:
if not item.is_mine_flag and item.status_code == 2:
item.setstatus(status_code=7)
elif item.is_mine_flag and item.status_code == 0:
item.setstatus(status_code=1)
mine_clicked.setstatus(status_code=6)
return True
mine_clicked.setstatus(status_code=1)
coords_around = self.getaround(y, x)
num_mines = 0
for (j, i) in coords_around:
num_mines += int(self.mines_matrix[j][i].is_mine_flag)
mine_clicked.setnumminesaround(num_mines)
if num_mines == 0:
for (j, i) in coords_around:
if self.mines_matrix[j][i].num_mines_around == -1:
self.openmine(i, j)
return False
'''获得坐标点的周围坐标点'''
def getaround(self, row, col):
coords = []
for j in range(max(0, row-1), min(row+1, self.cfg.GAME_MATRIX_SIZE[1]-1)+1):
for i in range(max(0, col-1), min(col+1, self.cfg.GAME_MATRIX_SIZE[0]-1)+1):
if j == row and i == col:
continue
coords.append((j, i))
return coords
'''是否正在游戏中'''
@property
def gaming(self):
return self.status_code == 0
'''被标记为雷的雷数目'''
@property
def flags(self):
num_flags = 0
for row in self.mines_matrix:
for item in row: num_flags += int(item.status_code == 2)
return num_flags
'''已经打开的雷的数目'''
@property
def openeds(self):
num_openeds = 0
for row in self.mines_matrix:
for item in row: num_openeds += int(item.opened)
return num_openeds

这里只解释几个可能有小伙伴看不太懂的地方:

打开雷的时候我们用了递归,作用是当点击到的方格周围都没有雷的时候,系统就自动打开这个方格周围的方格,以实现有时候点击一个方格可以打开一大片方格的效果,这里的周围都特指以目标方格为中心的九宫格内的所有方格;

鼠标左右键一起按在已经打开的方格上的话,如果这个方格周围的方格已经被标记为雷的数目和这个方格上显示的数字一致,就把这个方格周围未被标记为雷的方格都打开(所以如果你标记错的话,一起打开的时候会显示你游戏已经GG了)。

定义完这些游戏中必要的元素类之后就在游戏主函数里实例化它们:

# 实例化游戏地图
minesweeper_map = MinesweeperMap(cfg, images)
position = (cfg.SCREENSIZE[0] - int(cfg.GRIDSIZE * 1.25)) // 2, (cfg.GRIDSIZE * 2 - int(cfg.GRIDSIZE * 1.25)) // 2
emoji_button = EmojiButton(images, position=position)
fontsize = font.size(str(cfg.NUM_MINES))
remaining_mine_board = TextBoard(str(cfg.NUM_MINES), font, (30, (cfg.GRIDSIZE*2-fontsize[1])//2-2), cfg.RED)
fontsize = font.size('000')
time_board = TextBoard('000', font, (cfg.SCREENSIZE[0]-30-fontsize[0], (cfg.GRIDSIZE*2-fontsize[1])//2-2), cfg.RED)
time_board.is_start = False

然后写个游戏主循环以根据用户的操作来更新当前的游戏状态就可以了:

# 游戏主循环
clock = pygame.time.Clock()
while True:
screen.fill(cfg.BACKGROUND_COLOR)
# --按键检测
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = event.pos
mouse_pressed = pygame.mouse.get_pressed()
minesweeper_map.update(mouse_pressed=mouse_pressed, mouse_pos=mouse_pos, type_='down')
elif event.type == pygame.MOUSEBUTTONUP:
minesweeper_map.update(type_='up')
if emoji_button.rect.collidepoint(pygame.mouse.get_pos()):
minesweeper_map = MinesweeperMap(cfg, images)
time_board.update('000')
time_board.is_start = False
remaining_mine_board.update(str(cfg.NUM_MINES))
emoji_button.setstatus(status_code=0)
# --更新时间显示
if minesweeper_map.gaming:
if not time_board.is_start:
start_time = time.time()
time_board.is_start = True
time_board.update(str(int(time.time() - start_time)).zfill(3))
# --更新剩余雷的数目显示
remianing_mines = max(cfg.NUM_MINES - minesweeper_map.flags, 0)
remaining_mine_board.update(str(remianing_mines).zfill(2))
# --更新表情
if minesweeper_map.status_code == 1:
emoji_button.setstatus(status_code=1)
if minesweeper_map.openeds + minesweeper_map.flags == cfg.GAME_MATRIX_SIZE[0] * cfg.GAME_MATRIX_SIZE[1]:
minesweeper_map.status_code = 1
emoji_button.setstatus(status_code=2)
# --显示当前的游戏状态地图
minesweeper_map.draw(screen)
emoji_button.draw(screen)
remaining_mine_board.draw(screen)
time_board.draw(screen)
# --更新屏幕
pygame.display.update()
clock.tick(cfg.FPS)

文章到这里就结束了,感谢你的观看,Python24个小游戏系列,下篇文章分享消消乐小游戏

为了感谢读者们,我想把我最近收藏的一些编程干货分享给大家,回馈每一个读者,希望能帮到你们。

干货主要有:

① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

⑥ 两天的Python爬虫训练营直播权限

All done~私信获取完整源代码。。

往期回顾

Python实现2048小游戏

Python实现五子棋联机对战小游戏

Python实现炸弹人小游戏

Python实现经典吃豆豆小游戏

Python实现消消乐小游戏

Python实恐龙跳一跳小游戏现

Python实现简易版飞机大战小游戏

Python实现俄罗斯方块小游戏

Python实现外星人入侵小游戏

Python实现“小兔子和Bun”游戏

Python实现八音符小游戏

Python实现拼图小游戏

Python实现滑雪小游戏

Python实现经典90坦克大战

Python实现FlappyBird的小游戏

Python实现塔防小游戏

Python实现接水果和金币小游戏

Python实现推箱子小游戏

Python实现24点小游戏

Python实现乒乓球小游戏

Python实现打砖块小游戏

Python实现过迷宫小游戏

Python实现打地鼠小游戏

版权声明
本文为[Cooci]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000040479632

  1. 利用Python爬虫获取招聘网站职位信息
  2. Using Python crawler to obtain job information of recruitment website
  3. Several highly rated Python libraries arrow, jsonpath, psutil and tenacity are recommended
  4. Python装饰器
  5. Python实现LDAP认证
  6. Python decorator
  7. Implementing LDAP authentication with Python
  8. Vscode configures Python development environment!
  9. In Python, how dare you say you can't log module? ️
  10. 我收藏的有关Python的电子书和资料
  11. python 中 lambda的一些tips
  12. python中字典的一些tips
  13. python 用生成器生成斐波那契数列
  14. python脚本转pyc踩了个坑。。。
  15. My collection of e-books and materials about Python
  16. Some tips of lambda in Python
  17. Some tips of dictionary in Python
  18. Using Python generator to generate Fibonacci sequence
  19. The conversion of Python script to PyC stepped on a pit...
  20. Python游戏开发,pygame模块,Python实现扫雷小游戏
  21. Python game development, pyGame module, python implementation of minesweeping games
  22. Python实用工具,email模块,Python实现邮件远程控制自己电脑
  23. Python utility, email module, python realizes mail remote control of its own computer
  24. 毫无头绪的自学Python,你可能连门槛都摸不到!【最佳学习路线】
  25. Python读取二进制文件代码方法解析
  26. Python字典的实现原理
  27. Without a clue, you may not even touch the threshold【 Best learning route]
  28. Parsing method of Python reading binary file code
  29. Implementation principle of Python dictionary
  30. You must know the function of pandas to parse JSON data - JSON_ normalize()
  31. Python实用案例,私人定制,Python自动化生成爱豆专属2021日历
  32. Python practical case, private customization, python automatic generation of Adu exclusive 2021 calendar
  33. 《Python实例》震惊了,用Python这么简单实现了聊天系统的脏话,广告检测
  34. "Python instance" was shocked and realized the dirty words and advertisement detection of the chat system in Python
  35. Convolutional neural network processing sequence for Python deep learning
  36. Python data structure and algorithm (1) -- enum type enum
  37. 超全大厂算法岗百问百答(推荐系统/机器学习/深度学习/C++/Spark/python)
  38. 【Python进阶】你真的明白NumPy中的ndarray吗?
  39. All questions and answers for algorithm posts of super large factories (recommended system / machine learning / deep learning / C + + / spark / Python)
  40. [advanced Python] do you really understand ndarray in numpy?
  41. 【Python进阶】Python进阶专栏栏主自述:不忘初心,砥砺前行
  42. [advanced Python] Python advanced column main readme: never forget the original intention and forge ahead
  43. python垃圾回收和缓存管理
  44. java调用Python程序
  45. java调用Python程序
  46. Python常用函数有哪些?Python基础入门课程
  47. Python garbage collection and cache management
  48. Java calling Python program
  49. Java calling Python program
  50. What functions are commonly used in Python? Introduction to Python Basics
  51. Python basic knowledge
  52. Anaconda5.2 安装 Python 库(MySQLdb)的方法
  53. Python实现对脑电数据情绪分析
  54. Anaconda 5.2 method of installing Python Library (mysqldb)
  55. Python implements emotion analysis of EEG data
  56. Master some advanced usage of Python in 30 seconds, which makes others envy it
  57. python爬取百度图片并对图片做一系列处理
  58. Python crawls Baidu pictures and does a series of processing on them
  59. python链接mysql数据库
  60. Python link MySQL database