python小项目之拼图游戏
日期: 2020-07-06 分类: 个人收藏 470次阅读
文章目录
前言
pygame模块是跨平台 Python 模块,专为电子游戏设计。包含图像、声音。创建在 SDL 基础上,允许实时电子游戏研发而无需被低级语言,如 C 语言或是更低级的汇编语言束缚。基于这样一个设想,所有需要的游戏功能和理念都完全简化位游戏逻辑本身,所有的资源结构都可以由高级语言提供。使用pygame模块进行游戏的开发将大大减少开发者的工作量,提升开发效率。pygame模块中提供了许多的API接口供给开发者使用,pygame中包含的模块如表1所所示。本文将在pygame模块为基础下,开发一个拼图小游戏,将图片进行随机分割,并将分割后的图片绘制在pygame创建出来的窗口当中,游戏的难度设置为可选,根据选择的难度,程序将对图片进行不同的划分和显示。
模块名 | 功能 |
pygame.cdrom | 访问光驱 |
pygame.cursors | 加载光标 |
pygame.display | 访问设备显示 |
pygame.draw | 绘制形状、线和点 |
pygame.event | 管理事件 |
pygame.font | 使用字体 |
pygame.image | 加载和存储图片 |
pygame.joystick | 使用手柄或类似的东西 |
pygame.key | 读取键盘按键 |
pygame.mixer | 声音 |
pygame.mouse | 鼠标 |
pygame.movie | 播放视频 |
pygame.music | 播放音频 |
pygame.overlay | 访问高级视频叠加 |
pygame.rect | 管理矩形区域 |
pygame.sndarray | 操作声音数据 |
pygame.sprite | 操作移动图像 |
pygame.surface | 管理图像和屏幕 |
pygame.surfarray | 管理点阵图像数据 |
pygame.time | 管理时间和帧信息 |
pygame.transform | 缩放和移动图像 |
1. 系统结构
2.1 拼图的实现
一个拼图游戏的实现,可以按照如下几个步骤进行,流程图如图2.1所示。
(1)图片切割
(2)切割后的图片进行打乱
(3)控制图片块移动方式
(4)控制图片块移动方向
(5)判断拼图是否完成
2.2 使用到的pygame模块介绍
(1) pygame.font模块
功能:加载和显示字体
使用:pygame.font.Font() 创建 Font 对象,再调用Font类中方法生成某种字体的文字
方法 | 功能 |
pygame.font.Font.render() | 在一个新 Surface 对象上绘制文本 |
pygame.font.Font.size() | 确定多大的空间用于表示文本 |
pygame.font.Font.set_underline() | 控制文本是否用下划线渲染 |
pygame.font.Font.get_underline() | 检查文本是否绘制下划线 |
pygame.font.Font.set_bold() | 启动粗体字渲染 |
pygame.font.Font.get_bold() | 检查文本是否使用粗体渲染 |
pygame.font.Font.set_italic() | 启动斜体字渲染 |
pygame.font.Font.metrics() | 获取字符串参数每个字符的参数 |
pygame.font.Font.get_italic() | 检查文本是否使用斜体渲染 |
pygame.font.Font.get_linesize() | 获取字体文本的行高 |
pygame.font.Font.get_height() | 获取字体的高度 |
pygame.font.Font.get_ascent() | 获取字体顶端到基准线的距离 |
pygame.font.Font.get_descent() | 获取字体底端到基准线的距离 |
(2)pygame.time模块
功能:管理时间帧信息
使用:使用pygame.time.Clock()方法创建一个Clock对象来跟踪时间,调用类内部的方法来达到游戏显示动画帧的效果
Clock类内部包含的方法如表 2.2-2所示
方法 | 功能 |
pygame.time.Clock.tick() | 更新时钟 |
pygame.time.Clock.tick_busy_loop() | 更新时钟 |
pygame.time.Clock.get_time() | 在上一个tick中使用的时间 |
pygame.time.Clock.get_rawtime() | 在上一个tick中使用的实际时间 |
pygame.time.Clock.get_fps() | 计算时钟帧率 |
(3) pygame.image模块和 pygame.Surface模块
功能:加载图片文件,设置图片文件中的内容
使用:调用pygame.image.load()方法,加载一张图片,并返回一个Surface 对象,进而可以进行画线、设置像素、对特定的区域进行捕获。
方法 | 功能 |
pygame.Surface.blit() | 将一个图像(Surface 对象)绘制到另一个图像上方 |
pygame.Surface.convert() | 修改图像(Surface 对象)的像素格式 |
pygame.Surface.fill() | 使用纯色填充 Surface 对象 |
pygame.Surface.get_size() | 获取 Surface 对象的尺寸 |
pygame.Surface.get_width() | 获取 Surface 对象的宽度 |
pygame.Surface.get_height() | 获取 Surface 对象的高度 |
pygame.Surface.get_rect() | 获取 Surface 对象的矩形区域 |
(4) pygame.transform模块
功能:缩放和移动图像
使用:调用pygame.transform.scale()方法Surface对象带有图片数据进行缩放
(5)pygame.display模块
功能:控制窗口和屏幕显示
使用:调用pygame.display.set_mode方法,初始化一个准备显示的窗口,使用只需将将对应的文件数据填入其中便可以完成显示的工作
方法 | 功能 |
pygame.display.set_mode() | 初始化一个准备显示的窗口或屏幕 |
pygame.display.get_surface() | 获取当前显示的 Surface 对象 |
pygame.display.update() | 更新部分软件界面显示 |
pygame.display.set_caption() | 设置显示窗体的标题 |
(6)pygame.event模块
功能: 处理事件与事件队列
使用:调用pygame.event.get()方法从消息队列中获取一个事件,再去判断该事件的type属性 进一步去处理
3. 实现代码
3.1 拼图代码实现
(1)图片的分割与存储
对图片进行分割和打乱,采用列表存储图片分割后分割块的索引,用一维的列表表示二维拼图图块,根据列表中的索引值进行转换即可得到对应的图片分割块的位置,当整个列表有序时表示拼图完成,游戏结束。一维与二维转化如图3.1-1所示,对应关系为:
行号=(数值索引)/size 列号=(数值索引)%size //size为拼图的大小
图片分割具体实现代码,先对存储的列表进行初始化,将右下角的分割块设置为空白块,再使用random模块对列表进行随机打乱。
1. '''''
2. @函数名:CreateBoard
3. @函数作用:获得一个打乱的列表 并初始化空白图块的位置
4. @参数: num_rows:行数 num_cols:列数 num_cells:整张图的大小
5. @返回值:返回拼图列表和空白图块的位置
6. '''
7. def CreateBoard(num_rows, num_cols, num_cells):
8. board = []
9. for i in range(num_cells): board.append(i)
10.
11. '''''去掉右下角的一块 作为拼图游戏的空白块'''
12. blank_cell_idx = num_cells - 1
13. board[blank_cell_idx] = -1
14.
15. '''''
16. 从配置文件cfg.py中获取打乱次数NUMRANDOM
17. 使用random模块 获取0-3之间的随机数
18. 根据随机数值的不同调整board列表中的位置
19. 0: left, 1: right, 2: up, 3: down
20. '''
21. for i in range(cfg.NUMRANDOM):
22. direction = random.randint(0, 3)
23. if direction == 0: blank_cell_idx = moveL(board, blank_cell_idx, num_cols)
24. elif direction == 1: blank_cell_idx = moveR(board, blank_cell_idx, num_cols)
25. elif direction == 2: blank_cell_idx = moveU(board, blank_cell_idx, num_rows, num_cols)
26. elif direction == 3: blank_cell_idx = moveD(board, blank_cell_idx, num_cols)
27. return board, blank_cell_idx
判断整个游戏是否结束,遍历整个列表,判断该列表是否变得有序
1. '''
2. @函数名:isGameOver
3. @函数作用:判断游戏是否结束
4. @参数: board:拼图列表 size:大小(行数、列数)
5. @返回值:如果列表有序返回true 否则返回false
6. '''
7. def isGameOver(board, size):
8. assert isinstance(size, int)
9. num_cells = size * size
10.
11. #判断列表是否变为一个有序的状态
12. for i in range(num_cells-1):
13. if board[i] != i: return False
14. return True
(2)拼图分割块的移动
图片分割块的移动实质上是与空白图块进行交换位置,根据空白块的位置进行上下左右的移动,移动的时候要注意几个边界条件,当空白分割块在第一行、最后一行、第一列和最后一列的位置上时,分别对应无法向上、下、左和右移动。在进行位置交换时要先判断此次位置的移动是否合法。具体实现代码如下
1. '''''
2. @函数名:moveR
3. @函数作用:将空白图块和左边的图块进行位置交换
4. @参数: board:拼图列表 blank_cell_idx:空白图标位置 num_cols:拼图列表行数
5. @返回值:返回空白图块当前的位置
6. '''
7. def moveR(board, blank_cell_idx, num_cols):
8. #判断空白图块的位置是否在第一列上,如果在第一列上则不进行交换
9. if blank_cell_idx % num_cols == 0: return blank_cell_idx
10. #进行数值替换
11. board[blank_cell_idx-1], board[blank_cell_idx] = board[blank_cell_idx], board[blank_cell_idx-1]
12. return blank_cell_idx - 1
13.
14. '''''
15. @函数名:moveL
16. @函数作用:将空白图块和右边图块进行位置交换
17. @参数: board:拼图列表 blank_cell_idx:空白图标位置 num_cols:拼图列表行数
18. @返回值:返回空白图块当前的位置
19. '''
20. def moveL(board, blank_cell_idx, num_cols):
21. #判断空白图块是否在最后一列上,如果在最后一列则不进行交换
22. if (blank_cell_idx+1) % num_cols == 0: return blank_cell_idx
23. #进行数值上的替换
24. board[blank_cell_idx+1], board[blank_cell_idx] = board[blank_cell_idx], board[blank_cell_idx+1]
25. return blank_cell_idx + 1
26.
27. '''''
28. @函数名:moveD
29. @函数作用:将空白图块和上边图块进行位置交换
30. @参数: board:拼图列表 blank_cell_idx:空白图标位置 num_cols:拼图列表列数
31. @返回值:返回空白图块当前的位置
32. '''
33. def moveD(board, blank_cell_idx, num_cols):
34. #判断空白图块的位置是否在第一行上,如果在第一行上则不进行替换
35. if blank_cell_idx < num_cols: return blank_cell_idx
36. #进行数值上的替换
37. board[blank_cell_idx-num_cols], board[blank_cell_idx] = board[blank_cell_idx], board[blank_cell_idx-num_cols]
38. return blank_cell_idx - num_cols
39.
40. '''''
41. @函数名:moveU
42. @函数作用:将空白图块和下边图块进行位置交换
43. @参数: board:拼图列表 blank_cell_idx:空白图标位置 num_cols:拼图列表列数
44. @返回值:返回空白图块当前的位置
45. '''
46. def moveU(board, blank_cell_idx, num_rows, num_cols):
47. #判断当前空白图块是否在最后一行上 如果在则不进行替换
48. if blank_cell_idx >= (num_rows-1) * num_cols: return blank_cell_idx
49. #进行数值替换
50. board[blank_cell_idx+num_cols], board[blank_cell_idx] = board[blank_cell_idx], board[blank_cell_idx+num_cols]
51. return blank_cell_idx + num_cols
3.2 拼图交互和界面的实现
(1)游戏开始界面
使用pygame的font模块引入对应的字体文件,再通过render方法绘制出提示语句,并调用get_rect方法获取文本对象的属性值,设置其属性,最后将其绘制在对应的窗口上,并调用pygame的event模块,等待用户输入 选择对应的难度。具体实现代码如下所示:
1. '''''
2. @函数名:ShowStartInterface
3. @函数作用:显示游戏开始的界面
4. @参数: screen:pygame窗口对象 wight:宽度 height:高度
5. @返回值:返回游戏难度数值 3/4/5
6. '''
7. def ShowStartInterface(screen, width, height):
8. # 从配置文件中获取背景颜色并将窗口填满
9. screen.fill(cfg.BACKGROUNDCOLOR)
10. # 从配置文件中获取字体的路径 并设置tfront和cfront字体的大小
11. tfont = pygame.font.Font(cfg.FONTPATH, width//4)
12. cfont = pygame.font.Font(cfg.FONTPATH, width//20)
13.
14. # 配置提示语句,使用render方法绘制出一个文本
15. title = tfont.render('PUZZLE', True, cfg.GRAY)
16. content1 = cfont.render('按Q或W或E键开始游戏,退出游戏请按ESC', True, cfg.BLACK)
17. content2 = cfont.render('Q为3*3模式, W为4*4模式, E为5*5模式', True, cfg.BLACK)
18.
19. #返回对应文本对象的的属性对象 设置各个文本的位置
20. trect = title.get_rect()
21. trect.midtop = (width/2, height/10)
22. crect1 = content1.get_rect()
23. crect1.midtop = (width/2, height/2.2)
24. crect2 = content2.get_rect()
25. crect2.midtop = (width/2, height/1.8)
26.
27. #重新绘制窗口中的内容
28. screen.blit(title, trect)
29. screen.blit(content1, crect1)
30. screen.blit(content2, crect2)
31.
32. while True:
33. #从队列中获取事件
34. for event in pygame.event.get():
35. #退出/按下ESC 表示退出游戏
36. if (event.type == pygame.QUIT) or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
37. #卸载所有导入的 pygame 模块
38. pygame.quit()
39. sys.exit()
40. elif event.type == pygame.KEYDOWN:
41. #判断是否为q/w/e中的其中一个按键
42. if event.key == ord('q'): return 3
43. elif event.key == ord('w'): return 4
44. elif event.key == ord('e'): return 5
45. #更新窗口上的显示内容
46. pygame.display.update()
(2)游戏结束界面显示
与开始界面的显示差不多,通过绘制文本对象,将其显示在游戏的窗口上,等待用户输入并结束程序,实现代码如下:
1. '''''
2. @函数名:ShowEndInterface
3. @函数作用:显示游戏结束的界面
4. @参数: screen:pygame窗口对象 wight:宽度 height:高度
5. @返回值:none
6. '''
7. def ShowEndInterface(screen, width, height):
8. #从配置文件中获取背景颜色并将窗口填满
9. screen.fill(cfg.BACKGROUNDCOLOR)
10. #从配置文件中获取字体的路径 并设置字体的大小为windth的十五分之一
11. font = pygame.font.Font(cfg.FONTPATH, width//15)
12. #配置提示语句,使用render方法绘制出一个文本 (233, 150, 122)表示文本的颜色
13. title = font.render('恭喜! 你成功完成了拼图!', True, (233, 150, 122))
14. #返回title的属性对象
15. rect = title.get_rect()
16. #设置midtop数值,即为文本的位置
17. rect.midtop = (width/2, height/2.5)
18. #重新绘制窗口内容
19. screen.blit(title, rect)
20. #更新显示窗口中的内容
21. pygame.display.update()
22. while True:
23. #结束后,进行事件的轮询捕捉 等待用户操作 然后退出
24. for event in pygame.event.get():
25. #退出/空格键按下
26. if (event.type == pygame.QUIT) or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
27. pygame.quit()
28. sys.exit()
29. pygame.display.update()
(3)游戏运行界面
调用pygame.image模块加载图片,并通过transfrom模块对其进行缩放。
1. #根据配置文件cfg.py中的图片路径PICTURE_ROOT_DIR,对位图进行加载
2. game_img_used = pygame.image.load(GetImagePath(cfg.PICTURE_ROOT_DIR))
3. #根据cfg中设置屏幕大小 对加载位图进行缩放
4. game_img_used = pygame.transform.scale(game_img_used, cfg.SCREENSIZE)
5. #获取加载窗口的属性
6. game_img_used_rect = game_img_used.get_rect()
调用pygame.display模块创建一个窗口,设置其标题
1. # 根据cfg中的屏幕大小数据SCREENSIZE设置窗口的大小
2. screen = pygame.display.set_mode(cfg.SCREENSIZE)
3. #使用display模块中的set_caption方法设置窗口的标题
4. pygame.display.set_caption('PUZZLE_BY_SJ')
调用开始界面显示方法,根据用户的输入,设置拼图的行列,根据窗口的大小,计算每一个图片分割块的长度和宽度(行列的值默认相等)
1. #显示游戏开始界面,等待用户输入
2. size = ShowStartInterface(screen, game_img_used_rect.width, game_img_used_rect.height)
3. #判断获取到的返回值是否为int类型,否则退出程序
4. assert isinstance(size, int)
5. #设置游戏拼图的行列数值
6. num_rows, num_cols = size, size
7. #设置拼图块数目
8. num_cells = size * size
9. #根据窗口大小,计算每一个块的大小
10. cell_width = game_img_used_rect.width // num_cols
11. cell_height = game_img_used_rect.height // num_rows
内容显示,根据拼图列表中的值计算出对应的行列再转化为窗口上具体的区域,调用pygame.Rect方法设置显示的区域,再重新对窗口进行绘制,游戏中有一个主循环,每次循环要对拼图列表中的每一个值进行判断,并重新显示,遍历完拼图列表之后再调用pygame.draw模块绘画直线,将图片分割出层次感,实现代码如下:
1. screen.fill(cfg.WHITE)
2. #对窗口进行图片填充
3. for i in range(num_cells):
4. #表示空白图块 不进行图片填充
5. if game_board[i] == -1:
6. continue
7.
8. #计算当前图块在窗口上的位置 行号和列号,拼图列表是一个一维列表要对其转换为二维列表的表示方式
9. x_pos = i // num_cols
10. y_pos = i % num_cols
11.
12. #使用pygame.Rect设置显示的区域
13. rect = pygame.Rect(y_pos*cell_width, x_pos*cell_height, cell_width, cell_height)
14. img_area = pygame.Rect((game_board[i]%num_cols)*cell_width, (game_board[i]//num_cols)*cell_height, cell_width, cell_height)
15. #对窗口进行绘制
16. screen.blit(game_img_used, rect, img_area)
17. #在窗口中绘制直线 行上的直线 分割图片
18. for i in range(num_cols+1):
19. pygame.draw.line(screen, cfg.BLACK, (i*cell_width, 0), (i*cell_width, game_img_used_rect.height))
20. # 在窗口中绘制直线 列上的直线 分割图片
21. for i in range(num_rows+1):
22. pygame.draw.line(screen, cfg.BLACK, (0, i*cell_height), (game_img_used_rect.width, i*cell_height))
23. #更新窗口显示的内容
24. pygame.display.update()
游戏动画的运行调用pygame的Clock模块,控制动画显示的帧率,类似于延时,使每次循环在相同的间隔内执行
1. #创建时钟对象 (可以控制游戏循环频率)
2. clock = pygame.time.Clock()
3. #设置游戏动画的帧率 让程序知道多长时间绘制一次 以毫秒为单位
4. clock.tick(cfg.FPS)
(2)游戏的交互
交互的实现是调用pygame的event模块,从消息队列中取出一个事件,并对其进行判断,分为鼠标和键盘事件。键盘事件根据按键的输入分类为上下左右,根据不同的输入执行相对应移动函数,实现代码如下:
1. #键盘操作
2. elif event.type == pygame.KEYDOWN:
3. #左移
4. if event.key == pygame.K_LEFT or event.key == ord('a'):
5. blank_cell_idx = moveL(game_board, blank_cell_idx, num_cols)
6. #右移
7. elif event.key == pygame.K_RIGHT or event.key == ord('d'):
8. blank_cell_idx = moveR(game_board, blank_cell_idx, num_cols)
9. #上移
10. elif event.key == pygame.K_UP or event.key == ord('w'):
11. blank_cell_idx = moveU(game_board, blank_cell_idx, num_rows, num_cols)
12. #下移
13. elif event.key == pygame.K_DOWN or event.key == ord('s'):
14. blank_cell_idx = moveD(game_board, blank_cell_idx, num_cols)
鼠标的操作,先获取鼠标的焦点位置,计算该位置在整个窗口的哪一块位置,如果其上下左右存在空白图块,则进行移动,实现代码如下:
1. #鼠标操作
2. elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
3. #获取鼠标的焦点位置
4. x, y = pygame.mouse.get_pos()
5. #计算当前处在哪一个拼图块的位置
6. x_pos = x // cell_width
7. y_pos = y // cell_height
8.
9. #计算该块再拼图列表中的index
10. idx = x_pos + y_pos * num_cols
11. #在空白图块右边
12. if idx == blank_cell_idx-1:
13. blank_cell_idx = moveR(game_board, blank_cell_idx, num_cols)
14. #在空白图块的左边
15. elif idx == blank_cell_idx+1:
16. blank_cell_idx = moveL(game_board, blank_cell_idx, num_cols)
17. #在空白图块的上方
18. elif idx == blank_cell_idx+num_cols:
19. blank_cell_idx = moveU(game_board, blank_cell_idx, num_rows, num_cols)
20. #在空白图块的下方
21. elif idx == blank_cell_idx-num_cols:
22. blank_cell_idx = moveD(game_board, blank_cell_idx, num_cols)
- 实验效果
开始界面
选择3 * 3模式,按下Q键
移动拼图
选择4 * 4模式
选择5 * 5模式
随机图片更换
结束界面
5. 总结和展望
本次项目的设计比较简单,学习了pygame提供的开发接口,pygame提供的接口非常方便,缩短了一个游戏开发的周期,拼图游戏还能够进行优化和添加一些新的功能,后续可以对其进行改进。总的来讲,这是对python基本语法的一次总结和对新模块的学习,比较基础,没有特别困难的内容。自己对python这门语言中很多精髓的地方还没有去了解到,后面会继续学习,加强自身的实力。
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
上一篇: 如何在科研论文中画出漂亮的插图?
精华推荐