Python游戏开发,Pygame模块,Python从零开始带大家实现一个魔塔小游戏(2)

小雁子学Python 2021-10-27 16:37:28
Python 游戏 开发 模块 pygame

前言

这一期我们会带大家进一步复现我们的魔塔小游戏,主要内容包括英雄类的定义与其基础行动的实现,行动过程中触发不同层的切换等功能。

废话不多说,让我们愉快地开始吧~

开发工具

Python版本: 3.7.4

相关模块:

pygame模块;

以及一些python自带的模块。

环境搭建

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

原理简介

上一期,我们实现了游戏的基础画面定义,类似这样:

基础界面

细心的小伙伴肯定发现了,地图里怎么没有我们的勇士呢?没有他我们还怎么去拯救公主呀~别急,这期就带大家来实现这部分内容。

首先,我们来定义一下我们的英雄勇士类:

'''定义我们的主角勇士'''
class Hero(pygame.sprite.Sprite):
def __init__(self, imagepaths, blocksize, position, fontpath=None):
pygame.sprite.Sprite.__init__(self)
# 设置基础属性
self.font = pygame.font.Font(fontpath, 40)
# 加载对应的图片
self.images = {
}
for key, value in imagepaths.items():
self.images[key] = pygame.transform.scale(pygame.image.load(value), (blocksize, blocksize))
self.image = self.images['down']
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = position
# 设置等级等信息
self.level = 1
self.life_value = 1000
self.attack_power = 10
self.defense_power = 10
self.num_coins = 0
self.experience = 0
self.num_yellow_keys = 0
self.num_purple_keys = 0
self.num_red_keys = 0
'''将勇士绑定到屏幕上'''
def draw(self, screen):
screen.blit(self.image, self.rect)

将其绑定到游戏主界面之后的效果如下:

效果

看起来是不是哪里不对?没错,左边原来有文字显示勇士当前的状态呀!现在都没了!不过没关系,问题不大,我们写几行代码将英雄的信息显示在左边的面板上面即可:

font_renders = [
self.font.render(str(self.level), True, (255, 255, 255)),
self.font.render(str(self.life_value), True, (255, 255, 255)),
self.font.render(str(self.attack_power), True, (255, 255, 255)),
self.font.render(str(self.defense_power), True, (255, 255, 255)),
self.font.render(str(self.num_coins), True, (255, 255, 255)),
self.font.render(str(self.experience), True, (255, 255, 255)),
self.font.render(str(self.num_yellow_keys), True, (255, 255, 255)),
self.font.render(str(self.num_purple_keys), True, (255, 255, 255)),
self.font.render(str(self.num_red_keys), True, (255, 255, 255)),
]
rects = [fr.get_rect() for fr in font_renders]
rects[0].topleft = (160, 80)
for idx in range(1, 6):
rects[idx].topleft = 160, 127 + 42 * (idx - 1)
for idx in range(6, 9):
rects[idx].topleft = 160, 364 + 55 * (idx - 6)
for fr, rect in zip(font_renders, rects):
screen.blit(fr, rect)

效果是这样子的:

在这里插入图片描述

完成了勇士类最基础的定义,接下来我们就该让他动起来啦,具体而言,我们先实现一个勇士行动的类函数:

'''行动'''
def move(self, direction):
assert direction in self.images
self.image = self.images[direction]
move_vector = {

'left': (-self.blocksize, 0),
'right': (self.blocksize, 0),
'up': (0, -self.blocksize),
'down': (0, self.blocksize),
}[direction]
self.rect.left += move_vector[0]
self.rect.top += move_vector[1]

然后写个按键检测,并根据玩家按下的键值来决定勇士的行动方向:

key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_w] or key_pressed[pygame.K_UP]:
self.hero.move('up')
elif key_pressed[pygame.K_s] or key_pressed[pygame.K_DOWN]:
self.hero.move('down')
elif key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:
self.hero.move('left')
elif key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:
self.hero.move('right')

如果你觉得我上面的代码写的没问题,大功告成了,这样写是有两个问题的。

首先,这样子写会导致玩家按一次上键,勇士就移动很多格,导致玩家不好控制勇士的位置,此时我们可以添加一个行动冷却变量:

# 行动冷却
self.move_cooling_count = 0
self.move_cooling_time = 5
self.freeze_move_flag = False

在冷却中的时候进行计数:

if self.freeze_move_flag:
self.move_cooling_count += 1
if self.move_cooling_count > self.move_cooling_time:
self.move_cooling_count = 0
self.freeze_move_flag = False

计数完成后英雄方可恢复行动能力。于是move可以重写成:

'''行动'''
def move(self, direction):
if self.freeze_move_flag: return
assert direction in self.images
self.image = self.images[direction]
move_vector = {

'left': (-self.blocksize, 0),
'right': (self.blocksize, 0),
'up': (0, -self.blocksize),
'down': (0, self.blocksize),
}[direction]
self.rect.left += move_vector[0]
self.rect.top += move_vector[1]
self.freeze_move_flag = True

感兴趣的小伙伴可以自行去掉这段代码实际感受一下键盘操作我们的勇士时是否会存在区别。

另外一个问题,也是最严重的问题,那就是行动会不合法,比如勇士会出现在这样的位置:

在这里插入图片描述

因此,我们需要再添加额外的移动是否合法的判断:

'''行动'''
def move(self, direction, map_parser):
if self.freeze_move_flag: return
assert direction in self.images
self.image = self.images[direction]
move_vector = {
'left': (-1, 0), 'right': (1, 0), 'up': (0, -1), 'down': (0, 1)}[direction]
block_position = self.block_position[0] + move_vector[0], self.block_position[1] + move_vector[1]
if block_position[0] >= 0 and block_position[0] < map_parser.map_size[1] and \
block_position[1] >= 0 and block_position[1] < map_parser.map_size[0]:
if map_parser.map_matrix[block_position[1]][block_position[0]] in ['0']:
self.block_position = block_position
elif map_parser.map_matrix[block_position[1]][block_position[0]] in ['24']:
self.dealcollideevent(
elem=map_parser.map_matrix[block_position[1]][block_position[0]],
block_position=block_position,
map_parser=map_parser,
)
self.rect.left, self.rect.top = self.block_position[0] * self.blocksize + self.offset[0], self.block_position[1] * self.blocksize + self.offset[1]
self.freeze_move_flag = True

这里,为了方便判断,我们将原来采用的像素坐标改成了游戏地图中的元素块坐标(即上一期设计的游戏地图里,每个数字在地图矩阵中的位置索引)。另外,这里我们还需要想到的一个点是未来进一步复现游戏的过程中,我们需要在勇士和地图中一些元素发生碰撞时作出对应的响应,例如勇士和怪物进行决斗,捡到钥匙等等事件,因此我们也在上面的move函数中嵌入了dealcollideevent来处理这样的情况,一个简单效果展示如下:

在这里插入图片描述

当然,理论上按照原版的游戏这里应该是有一个背景故事的对话框的,这部分我们下一期再实现,本期我们主要实现一些基础的功能,比如一些简单事件的触发,包括遇到门,捡到钥匙等等:

'''处理撞击事件'''
def dealcollideevent(self, elem, block_position, map_parser):
# 遇到不同颜色的门, 有钥匙则打开, 否则无法前进
if elem in ['2', '3', '4']:
flag = False
if elem == '2' and self.num_yellow_keys > 0:
self.num_yellow_keys -= 1
flag = True
elif elem == '3' and self.num_purple_keys > 0:
self.num_purple_keys -= 1
flag = True
elif elem == '4' and self.num_red_keys > 0:
self.num_red_keys -= 1
flag = True
if flag: map_parser.map_matrix[block_position[1]][block_position[0]] = '0'
return flag
# 捡到不同颜色的钥匙
elif elem in ['6', '7', '8']:
if elem == '6': self.num_yellow_keys += 1
elif elem == '7': self.num_purple_keys += 1
elif elem == '8': self.num_red_keys += 1
map_parser.map_matrix[block_position[1]][block_position[0]] = '0'
return True
# 捡到宝石
elif elem in ['9', '10']:
if elem == '9': self.defense_power += 3
elif elem == '10': self.attack_power += 3
map_parser.map_matrix[block_position[1]][block_position[0]] = '0'
return True
# 遇到仙女, 进行对话, 并左移一格
elif elem in ['24']:
map_parser.map_matrix[block_position[1]][block_position[0] - 1] = elem
map_parser.map_matrix[block_position[1]][block_position[0]] = '0'
return False

最后,我们来实现一下勇士上下楼梯时切换当前游戏地图的效果,这咋听起来似乎有点难办,但其实不然,只需要将发生上下楼梯事件的命令返回到游戏主循环:

# 上下楼梯
elif elem in ['13', '14']:
if elem == '13': events = ['upstairs']
elif elem == '14': events = ['downstairs']
return True, events
'''行动'''
def move(self, direction, map_parser):
# 判断是否冷冻行动
if self.freeze_move_flag: return
assert direction in self.images
self.image = self.images[direction]
# 移动勇士
move_vector = {
'left': (-1, 0), 'right': (1, 0), 'up': (0, -1), 'down': (0, 1)}[direction]
block_position = self.block_position[0] + move_vector[0], self.block_position[1] + move_vector[1]
# 判断该移动是否合法, 并触发对应的事件
events = []
if block_position[0] >= 0 and block_position[0] < map_parser.map_size[1] and \
block_position[1] >= 0 and block_position[1] < map_parser.map_size[0]:
# --合法移动
if map_parser.map_matrix[block_position[1]][block_position[0]] in ['0']:
self.block_position = block_position
# --触发事件
elif map_parser.map_matrix[block_position[1]][block_position[0]] in ['2', '3', '4', '6', '7', '8', '9', '10', '13', '14', '24']:
flag, events = self.dealcollideevent(
elem=map_parser.map_matrix[block_position[1]][block_position[0]],
block_position=block_position,
map_parser=map_parser,
)
if flag: self.block_position = block_position
# 重新设置勇士位置
self.rect.left, self.rect.top = self.block_position[0] * self.blocksize + self.offset[0], self.block_position[1] * self.blocksize + self.offset[1]
# 冷冻行动
self.freeze_move_flag = True
# 返回需要在主循环里触发的事件
return events

然后在主循环中进行响应即可:

# --触发游戏事件
for event in move_events:
if event == 'upstairs':
self.map_level_pointer += 1
self.loadmap()
elif event == 'downstairs':
self.map_level_pointer -= 1
self.loadmap()

效果如下:

图片

不知道大家有没有发现一个问题,就是勇士上楼之后所在的位置其实不对,理论上应该是在当前地图的下楼梯口附近的,而不是上一张游戏地图里勇士最后上楼时所在的位置,那么这部分应该如何实现呢?其实很简单,一个简单的解决方案是在定义游戏地图的时候,在上下楼梯处定义一个00变量:

图片

画游戏地图的时候还是按照0元素去画:

if elem in self.element_images:
image = self.element_images[elem][self.image_pointer]
image = pygame.transform.scale(image, (self.blocksize, self.blocksize))
screen.blit(image, position)
elif elem in ['00', 'hero']:
image = self.element_images['0'][self.image_pointer]
image = pygame.transform.scale(image, (self.blocksize, self.blocksize))
screen.blit(image, position)

但是上下楼梯切换游戏地图时,我们可以利用该标识符重置角色所在的位置:

# --触发游戏事件
for event in move_events:
if event == 'upstairs':
self.map_level_pointer += 1
self.loadmap()
self.hero.placenexttostairs(self.map_parser, 'down')
elif event == 'downstairs':
self.map_level_pointer -= 1
self.loadmap()
self.hero.placenexttostairs(self.map_parser, 'up')

其中重置位置的函数实现如下:

'''放置到上/下楼梯口旁'''
def placenexttostairs(self, map_parser, stairs_type='up'):
assert stairs_type in ['up', 'down']
for row_idx, row in enumerate(map_parser.map_matrix):
for col_idx, elem in enumerate(row):
if (stairs_type == 'up' and elem == '13') or (stairs_type == 'down' and elem == '14'):
if row_idx > 0 and map_parser.map_matrix[row_idx - 1][col_idx] == '00':
self.block_position = col_idx, row_idx - 1
elif row_idx < map_parser.map_size[0] - 1 and map_parser.map_matrix[row_idx + 1][col_idx] == '00':
self.block_position = col_idx, row_idx + 1
elif col_idx > 0 and map_parser.map_matrix[row_idx][col_idx - 1] == '00':
self.block_position = col_idx - 1, row_idx
elif col_idx < map_parser.map_size[1] - 1 and map_parser.map_matrix[row_idx][col_idx + 1] == '00':
self.block_position = col_idx + 1, row_idx
self.rect.left, self.rect.top = self.block_position[0] * self.blocksize + self.offset[0], self.block_position[1] * self.blocksize + self.offset[1]

重新测试一下看看:

在这里插入图片描述

总结一下,主要就是实现了我们的勇士角色,以及他和地图中一些元素相遇后需要发生的一些简单的事件响应。本期完整源代码可以私信获取

文章到这里就结束了,感谢你的观看,Python29个小游戏系列,下篇文章分享魔塔小游戏呀(3)

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

往期回顾

Python从零开始带大家实现一个魔塔小游戏(1)

版权声明
本文为[小雁子学Python]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_43649691/article/details/120947882

  1. 【算法学习】237. 删除链表中的节点(java / c / c++ / python / go)
  2. 【算法学习】1672. 最富有客户的资产总量(java / c / c++ / python / go / rust)
  3. 【算法学习】771. 宝石与石头(java / c / c++ / python / go / rust)
  4. 【算法学习】02.03. 删除中间节点(java / c / c++ / python / go)
  5. 【算法学习】1769. 移动所有球到每个盒子所需的最小操作数(java / c / c++ / python / go / rust)
  6. 【算法学习】1486. 数组异或操作(java / c / c++ / python / go / rust)
  7. 【算法学习】LCP 44. 开幕式焰火(java / c / c++ / python / go / rust)
  8. 【算法学习】剑指 Offer 58 - II. 左旋转字符串(java / c / c++ / python / go / rust)
  9. python的学校疑问难题求解
  10. 大学python题 作业题 基础题
  11. Python字典的知识,输出的样例为,最高分:89
  12. python写入文件失败且程序提前中止
  13. 用Python写一个学生字典,帮帮忙
  14. Python,能不能帮帮忙,真的不会
  15. [python] yield 和 readline() 的使用问题
  16. python安装找不到问题救救孩子
  17. python中循环结构完成数字游戏
  18. 如何用python实现多列vlookup(excle操作)
  19. python语言deLong‘s test:通过统计学的角度来比较两个ROC曲线、检验两个ROC曲线的差异是否具有统计显著性
  20. LPC55S69 MicroPython模组和库函数
  21. LPC55S69 IoT Kit专属 Micropython模组和库函数简介
  22. 安装LPC55S69 MicroPython模块是遇到的CDC Interface驱动问题
  23. 使用soundcard在Python中操作声卡
  24. 自动化快速上手--Python(7)--【字典】--每天半小时
  25. Python之循环结构【包括列表、for语句、range()函数、while语句、循环嵌套、break、continue、算法优化等】
  26. Python模块安装与异常处理详解(numpy、pygame、matplotlib等)
  27. Python__init__.py作用
  28. python 爬取网页时出现多种错误
  29. Python中关于大量绘制速度曲线的问题
  30. python-async的安装和使用方法
  31. Matlab的fread(fild,1,int32)迁移到python变成什么
  32. 想用python开发一个音频过滤器,请指导?
  33. python使用openpyxl读取Excel文件显示No such file or directory
  34. xmoji虚拟头像交互如何使用python(像深度学习)制作?
  35. python 打开页面页面的链接,为什么总是报错呀?
  36. Python中DataLoader的batch_size、shuffle的疑惑。
  37. python安装pymssql库,可以import,但无法调用函数
  38. 【Python学习教程】常用的8个Python数据可视化库!
  39. python处理csv中的时间
  40. 数据结构,元音统计(Python)
  41. python的site-packages复制直接到其他电脑环境上能用吗
  42. Pycharm如何给项目配置python解释器
  43. conda创建python虚拟环境
  44. Python selenium的爬虫无法完整爬取整个页面的内容
  45. 高清版!这18张 Python 数据科学速查表,让你的代码变得更强大!
  46. python代码不会敲,请好心老哥帮助我一下
  47. Python敲七输出符合的个数
  48. Python 有人能给提供简单的思路嘛
  49. python单次运行写入csv成功,循环写入失败
  50. python利用os模块进行增量备份
  51. 【算法学习】807. 保持城市天际线(java / c / c++ / python / go / rust)
  52. 如何利用python输出等腰杨辉三角
  53. python按键执行倒计时小程序不能实现要求,要怎么改才好?
  54. Python request模块post请求的问题
  55. Django连接已有Oracle时的主键设置问题,没主键无法查询怎么办?
  56. 如何用python的dictionary编写一个联系人通讯录程序
  57. 如果Python里range反向输出,不输出步长会怎么样?
  58. 一个关于Python pip的问题: 出现Cannot open \python\Scripts\pip-script.py报错
  59. 富婆闺蜜非让我用Python给她写个淘宝双十一抢购脚本,那只能安排了
  60. 【全网最全】python正则表达式大全,所有讲解都在这,包教包会,学不会找我!