导语

上一期我们带大家完成了魔塔游戏每一层的初始化画面的制作:
童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(1)

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

相关文件

相关游戏素材(图片和音频等)源于网络,侵歉删。

对了大家可以关注小编的公众号哟~~
有很多资源可以领取的哟!!

Python日志

开发工具

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 = 1self.life_value = 1000self.attack_power = 10self.defense_power = 10self.num_coins = 0self.experience = 0self.num_yellow_keys = 0self.num_purple_keys = 0self.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)

效果是这样子的:


这样看起来就舒服多了。或许有小伙伴会问了,你绑定的位置坐标都是咋确定的呀?这个嘛,就是简单地试几个坐标,然后目测一下准不准该怎么调整坐标值就行了,如果你不嫌麻烦,也可以在画图软件里直接查看对应位置的像素坐标,都是ok的,条条大路通罗马嘛~

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

'''行动'''
def move(self, direction):assert direction in self.imagesself.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 += 1if self.move_cooling_count > self.move_cooling_time:self.move_cooling_count = 0self.freeze_move_flag = False

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

'''行动'''
def move(self, direction):if self.freeze_move_flag: returnassert direction in self.imagesself.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: returnassert direction in self.imagesself.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_positionelif 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 = Falseif elem == '2' and self.num_yellow_keys > 0:self.num_yellow_keys -= 1flag = Trueelif elem == '3' and self.num_purple_keys > 0:self.num_purple_keys -= 1flag = Trueelif elem == '4' and self.num_red_keys > 0:self.num_red_keys -= 1flag = Trueif 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 += 1elif elem == '7': self.num_purple_keys += 1elif elem == '8': self.num_red_keys += 1map_parser.map_matrix[block_position[1]][block_position[0]] = '0'return True# 捡到宝石elif elem in ['9', '10']:if elem == '9': self.defense_power += 3elif elem == '10': self.attack_power += 3map_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] = elemmap_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: returnassert direction in self.imagesself.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 += 1self.loadmap()elif event == 'downstairs':self.map_level_pointer -= 1self.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 += 1self.loadmap()self.hero.placenexttostairs(self.map_parser, 'down')elif event == 'downstairs':self.map_level_pointer -= 1self.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 - 1elif 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 + 1elif col_idx > 0 and map_parser.map_matrix[row_idx][col_idx - 1] == '00':self.block_position = col_idx - 1, row_idxelif 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_idxself.rect.left, self.rect.top = self.block_position[0] * self.blocksize + self.offset[0], self.block_position[1] * self.blocksize + self.offset[1]

重新测试一下看看:
那今天就写到这里呗,总结一下,主要就是实现了我们的勇士角色,以及他和地图中一些元素相遇后需要发生的一些简单的事件响应。

本期完整源代码可以在这里获取到:

公众号Python日志

下期内容应该会是本系列教程最有意思的一部分,即对战效果和对话效果以及一些物品提示的实现,大家可以多多关注/星标公众号,第一次时间看到相关的推文呀~

童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(2)相关推荐

  1. 童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(3)

    导语 上一期我们主要带大家写了勇士类,以及勇士与一些简单的地图元素接触时所触发的事件: 童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(2) 这一期我们会带大家进一步复现我们的魔塔小 ...

  2. 童年经典回忆 | 从零开始带大家用Python撸一个魔塔小游戏呀(1)

    相关文件 相关游戏素材(图片和音频等)源于网络,侵歉删. 需要源代码的小伙伴私信小编,或者关注小编的公众号[Python日志] 开发工具 Python版本:3.7.4 相关模块: pygame模块: ...

  3. 玩过八音符游戏吗?今天教大家用python做一个八音符游戏

    先来看一下效果图: 需要用到的东西 Python3.6 cocos2d模块 pyaudio模块 以及一些Python自带的模块 cocos2d模块用于搭建游戏框架 pyaudio模块用于获得麦克风的声 ...

  4. 今天带大家用Python制作一个FlappyBird(飞翔的小鸟)的小游戏

    相关文件 源码分享,问题解答!! 关注小编,私信小编领取哟! 当然别忘了一件三连哟~~ 对了大家可以关注小编的公众号哟~~ 有很多资源可以领取的哟!! 大家也可以去b站看我的一些教学视频哟!! 更多P ...

  5. python自动填写小程序表单_新年好!教大家用Python写一个自动回复拜年信息的小程序!...

    原标题:新年好!教大家用Python写一个自动回复拜年信息的小程序! 过年期间,想必大家都收到很多拜年信息吧!有没有也被拜年短信(大部分是群发)搞得很焦虑?不回复似乎显得很没有礼貌,一一回复又累心劳神 ...

  6. python制作阴阳师脚本_利用python制作一个阴阳师小游戏

    利用python制作一个阴阳师小游戏 发布时间:2020-11-27 13:59:49 来源:亿速云 阅读:84 这期内容当中小编将会给大家带来有关利用python制作一个阴阳师小游戏,文章内容丰富且 ...

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

    开发工具 Python版本: 3.7.4 相关模块: pygame模块: 以及一些python自带的模块. 环境搭建 安装Python并添加到环境变量,pip安装需要的相关模块即可. 原理简介 首先, ...

  8. python连连看小游戏_利用Python制作一个连连看小游戏,边学边玩!

    导语 今天我们将制作一个连连看小游戏,让我们愉快地开始吧~ 开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块 环境搭建 安装Python并添加到环 ...

  9. 请用python编写一个斗地主小游戏,并给出完整代码

    很高兴为您服务!这是一个使用Python编写的斗地主小游戏的完整代码:# 导入必备模块 import random# 定义一副牌 poker = ['A', 2, 3, 4, 5, 6, 7, 8, ...

最新文章

  1. AI语音算法“个性”强 与芯片端“磨合”挑战多
  2. 【Docker】docker安装redis
  3. win7下安装Oracle10g解决方案
  4. eps如何建立立体白模_从几个方面说说自动化立体仓库的特别之处
  5. linux 32位平台,文件大小受限于2G的解决方法
  6. 大学英语四六各项分值
  7. 计算机VFP输出方式有哪几种,VFP导出数据的方法大全
  8. MfC基础--绘图基础--win32
  9. 尔雅 科学通史(吴国盛) 个人笔记及课后习题 2018 第七章 实验传统的兴起
  10. 无约束多元函数求极值
  11. 【渝粤题库】陕西师范大学201951 明清小说史 作业
  12. RabbitMQ,手动ACK情况下,消费消息的时候出现异常,如何手动ACK或NACK
  13. U3D记腾讯面试经历
  14. opencv将16位灰度图片转化为8位
  15. 李智慧 - 架构师训练营 第四周
  16. 希腊神话传说中的诸神
  17. mysql dump 1449_mysqldump1449错误解决办法
  18. 自考本科英语(二)学习笔记和考试经验
  19. WPF自学手册-读书笔记(三)小有所成
  20. TusharePro快速入门

热门文章

  1. 25.多目标规划以及综合评价
  2. 微信小程序相同商品但是不同价格不同规格加入购物车方法
  3. 基于Python新生报到系统设计与实现 开题报告
  4. 3. 云计算中的网络基础知识
  5. mysql分表准则_Mysql分表准则_MySQL
  6. 一个球从10米高空落下,每次落下跳回到原来的一半,再落下,求第10次落地时,共经过多少米?
  7. python can总线_Arduino CAN总线实验结果
  8. Docker 三大核心之容器 之一 docker ps
  9. 二本考南方科技大学计算机,南方科技大学是一本还是二本大学
  10. 【MySQL】MySQL表的CRUD操作(基础)