功能介绍

人物行走

人物的行走速度这边分成水平方向(X轴)和竖直方向(Y轴),水平方向的速度要考虑加速度和摩擦力,竖直方向的速度要考虑重力加速度。水平方向:设定X轴向右走的速度为大于0,向左走的速度为小于0

竖直方向:设定Y轴向下的速度为大于0,向上的速度为小于0

游戏中的人物有下面几个主要的状态:站立不动:水平方向速度为0,且竖直方向站在某个物体上。

向左或向右走:水平方向速度的绝对值大于0,且竖直方向站在某个物体上。

向上跳:竖直方向方向速度小于0,且上方没有碰到某个物体,同时需要玩家按住jump键。

向下降落:竖直方向方向速度大于0或者玩家没有按住jump键,且下方没有碰到某个物体。

向上跳和向下降落的状态判断可能一开始比较难理解,可以看后面的具体实现,目的是如果玩家长按jump键时,可以让人物跳的更高。

上面的判断是否站在某个物体上,或者是否碰到某个物体,就需要用到物体之间的碰撞检测。

学习python可加交流群:887934385 分享视频教程

碰撞检测

对于游戏中出现的每一样东西,比如砖块,箱子,水管,地面,还有人物都可以看成是一个独立的物体,所以每个物体类都继承了pygame的精灵类pg.sprite.Sprite,可以使用精灵类提供的碰撞检测函数来判断。

设置source\constants.py 中的变量DEBUG值为True,可以看到图1的游戏截图,比如最简单的地面,可以看成是一个长方形的物体。下方红色的长方形物体就是地面(ground)

右边的几个红色小方块是阶梯(step)

左边空中的像墙一样的是砖块(brick)

带问号的是箱子(box)

因为人物是站在地面上,且水平速度为0,所以当前的人物状态就是站立不动。

游戏代码

游戏实现代码的github链接 超级玛丽

这边是csdn的下载链接 超级玛丽

代码介绍

人物行走代码

有一个单独的人物类,在source\components\player.py 中,其中有个handle_state 函数,根据人物当前的状态执行不同的函数。

为了简洁下面所有函数中将不相关的代码都省略掉了。

def handle_state(self, keys, fire_group):

if self.state == c.STAND:

self.standing(keys, fire_group)

elif self.state == c.WALK:

self.walking(keys, fire_group)

elif self.state == c.JUMP:

self.jumping(keys, fire_group)

elif self.state == c.FALL:

self.falling(keys, fire_group)

人物的状态就是上面说的4个状态:站立不动:c.STAND

向左或向右走:c.WALK

向上跳:c.JUMP

向下降落:c.FALL

人物类关于行走速度的成员变量先了解下:

水平方向相关的x_accel:水平方向的加速度,值大于0,不区别方向。

max_x_vel:水平方向的最大速度,值大于0,不区别方向。

x_vel:水平方向的速度,值大于0表示向右走,值小于0表示向左走。

初始值:max_run_vel和max_walk_vel 表示最大速度,run_accel和walk_accel表示加速度。

facing_right:值为True表示当前是向右走,值为False表示当前是向左走,这个是用来设置人物的图像。

竖直方向相关的gravity:重力加速度,值大于0,表示方向向下。

jump_vel:起跳时竖直方向的初始速度,值小于0,表示方向向上。

y_vel:竖直方向的速度。

看下最复杂的 walking 函数,keys数组是当前按下的键盘输入,tools.keybinding中值的含义如下

keybinding = {

'action':pg.K_s,

'jump':pg.K_a,

'left':pg.K_LEFT,

'right':pg.K_RIGHT,

'down':pg.K_DOWN

}先根据当前是否有按下 keybinding[‘action’] 键来设置不同的最大水平方向速度和水平方向加速度。

如果有按下 keybinding[‘jump’] 键,则设置人物状态为c.JUMP,初始化竖直方向的速度

如果有按下keybinding[‘left’]键,表示要向左走,如果 x_vel 大于0,表示之前是向右走的,所以设置一个转身的加速度为SMALL_TURNAROUND,然后调用cal_vel 函数根据之前的速度和加速度,计算出当前的速度。

如果有按下keybinding[‘right’]键,表示要向右走,和上面类似

如果没有按下keybinding[‘left’]键和keybinding[‘right’]键,就像有摩擦力的存在,则水平方向的速度会慢慢变成0,如果 x_vel 值为0,则设置人物状态为c.STAND。

def walking(self, keys, fire_group):

if keys[tools.keybinding['action']]:

self.max_x_vel = self.max_run_vel

self.x_accel = self.run_accel

else:

self.max_x_vel = self.max_walk_vel

self.x_accel = self.walk_accel

if keys[tools.keybinding['jump']]:

if self.allow_jump:

self.state = c.JUMP

if abs(self.x_vel) > 4:

self.y_vel = self.jump_vel - .5

else:

self.y_vel = self.jump_vel

if keys[tools.keybinding['left']]:

self.facing_right = False

if self.x_vel > 0:

self.frame_index = 5

self.x_accel = c.SMALL_TURNAROUND

self.x_vel = self.cal_vel(self.x_vel, self.max_x_vel, self.x_accel, True)

elif keys[tools.keybinding['right']]:

self.facing_right = True

if self.x_vel < 0:

self.frame_index = 5

self.x_accel = c.SMALL_TURNAROUND

self.x_vel = self.cal_vel(self.x_vel, self.max_x_vel, self.x_accel)

else:

if self.facing_right:

if self.x_vel > 0:

self.x_vel -= self.x_accel

else:

self.x_vel = 0

self.state = c.STAND

else:

if self.x_vel < 0:

self.x_vel += self.x_accel

else:

self.x_vel = 0

self.state = c.STAND

def cal_vel(self, vel, max_vel, accel, isNegative=False):

""" max_vel and accel must > 0 """

if isNegative:

new_vel = vel * -1

else:

new_vel = vel

if (new_vel + accel) < max_vel:

new_vel += accel

else:

new_vel = max_vel

if isNegative:

return new_vel * -1

else:

return new_vel

再看下jumping 函数,开始gravity 设为 c.JUMP_GRAVITY,可以看到JUMP_GRAVITY 比GRAVITY值小很多,如果玩家长按jump键时,可以让人物跳的更高。

如果竖直方向速度y_vel 大于0,表示方向向下,则设置人物状态为c.FALL

如果按下 keybinding[‘left’]键或 keybinding[‘right’]键,则计算水平方向的速度。

如果没有按 keybinding[‘jump’]键,则设置人物状态为c.FALL

JUMP_GRAVITY = .31

GRAVITY = 1.01

def jumping(self, keys, fire_group):

""" y_vel value: positive is down, negative is up """

self.allow_jump = False

self.frame_index = 4

self.gravity = c.JUMP_GRAVITY

self.y_vel += self.gravity

if self.y_vel >= 0 and self.y_vel < self.max_y_vel:

self.gravity = c.GRAVITY

self.state = c.FALL

if keys[tools.keybinding['right']]:

self.x_vel = self.cal_vel(self.x_vel, self.max_x_vel, self.x_accel)

elif keys[tools.keybinding['left']]:

self.x_vel = self.cal_vel(self.x_vel, self.max_x_vel, self.x_accel, True)

if not keys[tools.keybinding['jump']]:

self.gravity = c.GRAVITY

self.state = c.FALL

standing函数和 falling 函数比较简单,就省略了。

碰撞检测代码

人物的碰撞检测代码在 source\states\level.py 中的入口是update_player_position函数 ,可以看到这边分成水平方向和竖直方向:根据人物的水平方向速度x_vel 更新人物的X轴位置,同时人物的X轴位置不能超出游戏地图的X轴范围,然后调用check_player_x_collisions函数进行水平方向的碰撞检测。

根据人物的水平方向速度x_vel 更新人物的X轴位置,同时人物的X轴位置不能超出游戏地图的X轴范围,然后调用check_player_x_collisions函数进行水平方向的碰撞检测。

根据人物的竖直方向速度y_vel 更新人物的Y轴位置,然后调用check_player_y_collisions函数进行竖直方向的碰撞检测

def update_player_position(self):

self.player.rect.x += round(self.player.x_vel)

if self.player.rect.x < self.start_x:

self.player.rect.x = self.start_x

elif self.player.rect.right > self.end_x:

self.player.rect.right = self.end_x

self.check_player_x_collisions()

if not self.player.dead:

self.player.rect.y += round(self.player.y_vel)

self.check_player_y_collisions()

具体实现时将同一类物体放在一个pygame.sprite.Group类中,

pygame.sprite.Group

A container class to hold and manage multiple Sprite objects.

Group(*sprites) -> Group

这样每次调用pg.sprite.spritecollideany 函数就能判断人物和这一类物体是否有碰撞。

pygame.sprite.spritecollideany()

Simple test if a sprite intersects anything in a group.

spritecollideany(sprite, group, collided = None) -> Sprite Collision with the returned sprite.

spritecollideany(sprite, group, collided = None) -> None No collision

不同物体的group如下,另外敌人,金币和蘑菇等物体的碰撞检测先忽略。ground_step_pipe_group:地面,阶梯和水管的group。

brick_group:砖块的group, 如果是金币砖块,从下面碰撞会获取金币。

box_group:箱子的group,从下面碰撞箱子可以出现金币,蘑菇,花等的奖励。

因为不同种类group撞击时,后续产生的结果会有区别,所有需要对每一类group分别进行碰撞检测。

X轴方向上面3类group如果检测到有碰撞时,会调用adjust_player_for_x_collisions 函数,来调整人物的X轴位置。

def check_player_x_collisions(self):

ground_step_pipe = pg.sprite.spritecollideany(self.player, self.ground_step_pipe_group)

brick = pg.sprite.spritecollideany(self.player, self.brick_group)

box = pg.sprite.spritecollideany(self.player, self.box_group)

...

if box:

self.adjust_player_for_x_collisions(box)

elif brick:

self.adjust_player_for_x_collisions(brick)

elif ground_step_pipe:

if (ground_step_pipe.name == c.MAP_PIPE and

ground_step_pipe.type == c.PIPE_TYPE_HORIZONTAL):

return

self.adjust_player_for_x_collisions(ground_step_pipe)

elif powerup:

...

elif enemy:

...

elif coin:

...

adjust_player_for_x_collisions 函数先根据人物和碰撞物体的X轴相对位置,判断人物在碰撞物体的左边还是右边,来调整人物的X轴位置,然后设置人物水平方向的速度为0。

def adjust_player_for_x_collisions(self, collider):

if collider.name == c.MAP_SLIDER:

return

if self.player.rect.x < collider.rect.x:

self.player.rect.right = collider.rect.left

else:

self.player.rect.left = collider.rect.right

self.player.x_vel = 0

check_player_y_collisions 函数也是对不同group分别进行碰撞检测,Y轴方向这3类group如果检测到有碰撞时,会调用adjust_player_for_y_collisions 函数,来调整人物的Y轴位置。 最后调用check_is_falling函数判断人物是否要设成 向下降落 的状态。

def check_player_y_collisions(self):

ground_step_pipe = pg.sprite.spritecollideany(self.player, self.ground_step_pipe_group)

# decrease runtime delay: when player is on the ground, don't check brick and box

if self.player.rect.bottom < c.GROUND_HEIGHT:

brick = pg.sprite.spritecollideany(self.player, self.brick_group)

box = pg.sprite.spritecollideany(self.player, self.box_group)

brick, box = self.prevent_collision_conflict(brick, box)

else:

brick, box = False, False

if box:

self.adjust_player_for_y_collisions(box)

elif brick:

self.adjust_player_for_y_collisions(brick)

elif ground_step_pipe:

self.adjust_player_for_y_collisions(ground_step_pipe)

elif enemy:

...

elif shell:

...

self.check_is_falling(self.player)

adjust_player_for_y_collisions 函数先根据人物和碰撞物体的Y轴相对位置,判断人物在碰撞物体的下边还是上边,来调整人物的Y轴位置如果人物在碰撞物体的下边,则有一个反弹的效果,设置人物的竖直方向速度为7,调整人物的Y轴位置,设置人物状态为c.FALL。如果碰撞物体为砖块或箱子,还要进行后续处理。

如果人物在碰撞物体的上边,设置人物的竖直方向速度为0,调整人物的Y轴位置,一般情况下设置人物状态为c.WALK。

def adjust_player_for_y_collisions(self, sprite):

if self.player.rect.top > sprite.rect.top:

if sprite.name == c.MAP_BRICK:

...

elif sprite.name == c.MAP_BOX:

...

elif (sprite.name == c.MAP_PIPE and

sprite.type == c.PIPE_TYPE_HORIZONTAL):

return

self.player.y_vel = 7

self.player.rect.top = sprite.rect.bottom

self.player.state = c.FALL

else:

self.player.y_vel = 0

self.player.rect.bottom = sprite.rect.top

if self.player.state == c.FLAGPOLE:

self.player.state = c.WALK_AUTO

elif self.player.state == c.END_OF_LEVEL_FALL:

self.player.state = c.WALK_AUTO

else:

self.player.state = c.WALK

check_is_falling函数 判断人物下方是否有物体,有个小技巧,就是先将人物的Y轴位置向下移动1,然后判断和上面三类group是否有碰撞:如果没有碰撞,表示人物下方没有物体,这时候如果人物状态不是 c.JUMP 和一些特殊状态,就设置人物状态为 c.FALL。

如果有碰撞,则不用管。

最后将人物的Y轴位置恢复(向上移动1)。

def check_is_falling(self, sprite):

sprite.rect.y += 1

check_group = pg.sprite.Group(self.ground_step_pipe_group,

self.brick_group, self.box_group)

if pg.sprite.spritecollideany(sprite, check_group) is None:

if (sprite.state == c.WALK_AUTO or

sprite.state == c.END_OF_LEVEL_FALL):

sprite.state = c.END_OF_LEVEL_FALL

elif (sprite.state != c.JUMP and

sprite.state != c.FLAGPOLE and

not self.in_frozen_state()):

sprite.state = c.FALL

sprite.rect.y -= 1

代码已打包,学习python可加交流群:887934385 分享视频教程

python人物代码_Python 超级玛丽代码实现:人物行走和碰撞检测相关推荐

  1. python保存变量_python – 在代码运行之间保存变量的数据

    对于一个学校项目,我正在用 Python制作一个刽子手游戏.现在我的代码从字典中选择一个单词,如下所示: WordList = ["cat", "hat", & ...

  2. python写了代码_Python写代码的用法建议

    1.Mutable and immutable types Python有两种内置或用户定义的类型 可变类型是允许就地修改内容的类型.典型的可变列表是列表和词典:所有列表都有变异方法,如 list.a ...

  3. python复利计算_python复利代码

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 比如在<零基础学编程012:画出复利曲线图>提到的numpy和mat ...

  4. python折叠代码_Python实现代码块儿折叠

    Python实现代码块儿折叠 在python中方法和类都是可以折叠的,但是很多时候需要按照实现的功能将一部分代码折叠起来. 可以用着样的注释代码实现: # ''' your code ''' # 很简 ...

  5. python画画的代码_python画画代码

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! life is short, just use python.自从08年接触py ...

  6. python只能用一行代码_Python 一行代码能实现丧心病狂的功能

    手头有 109 张头部 CT 的断层扫描图片,我打算用这些图片尝试头部的三维重建.基础工作之一,就是要把这些图片数据读出来,组织成一个三维的数据结构(实际上是四维的,因为每个像素有 RGBA 四个通道 ...

  7. python 小甲鱼 代码_Python小代码

    先自我介绍一下,本人是正在自学Python的小白,没事分享一下自己写的小代码,欢迎在评论区补充. 游戏管理系统: 代码如下: def healthe(m):if m=="Y"or ...

  8. python撩妹代码_Python十行代码让你秒变撩妹达人!想学?

    做微信聊天机器人,实现步骤: 1.获取微信的使用权,即python脚本能控制微信收发信息. 2.python脚本收到聊天信息后,要对该信息进行处理,返回机器人的回应信息. 简易版代码 from wxp ...

  9. 怎么查看python文件的代码_python实现代码查看列举目录下的文件

    本节主要内容: python os模块.glob模块列出文件夹下所有文件. 共提供4个方法列出文件夹内的所有文件. 例子: 复制代码代码示例: #方法1:使用os.listdir import os ...

最新文章

  1. 企业号微信支付 公众号支付 H5调起支付API示例代码 JSSDK C# .NET
  2. 《漫画算法2》源码整理-6 两数之和 三数之和
  3. 【Keras】学习笔记(一)
  4. ie浏览器怎样查看html,查看IE浏览器历史记录的方法
  5. mysql虚拟列(Generated Columns)及JSON字段类型的使用
  6. 剑指 Offer 37. 序列化二叉树
  7. jitter 如何优化网络_网络推广如何做好网站SEO优化
  8. 零基础如何用 15 行 Python 代码搞定网易云热门歌单?
  9. python getattr用法_python自省函数getattr的用法
  10. 《STL源代码分析》---stl_stack.h读书笔记
  11. 分享一个手游脚本源码
  12. 微信php带菜单事件,phpcms微信插件自定义菜单绑定栏目获取消息的实现与应用
  13. peewee mysql_peewee基本使用
  14. Large Division-----同余模定理(大数的求余)
  15. ios UIView全部API解读
  16. 新氧云原生全栈数仓最佳实践
  17. python27.dll是系统自带的吗_Windows 10自带6款超强工具!好用又免费!
  18. java编程思想读书笔记汇总
  19. SAP中序时账导出操作过程指导
  20. 去字节面试,直接让人出门左拐:Bean 生命周期都不知道!

热门文章

  1. 微信什么情况下会出现“对方正在输入”?
  2. 【内存泄漏】使用memwatch高效排查代码的内存泄漏问题
  3. 自学笔记-SpringBoot集成ElasticSearch
  4. 一文详解C++多线程
  5. SQL Exists运算符
  6. 开发和常用工具推荐清单 转载
  7. 设置输入框和按钮对齐
  8. 基于51单片机的红外感应的智能垃圾桶设计
  9. 12款最强辅助IDEA插件,助力新手程序员
  10. STEM科学实验:测试了2000+儿童后证明它秒杀了90%的玩具!