作者 | marble_xu
责编 | 郭芮
出品 | CSDN博客
在《如何用 Python 实现超级玛丽的界面和状态机?》这篇文章中我们讲解了如何用代码实现界面和状态机,本文详解人物行走和碰撞检测的实现。
功能介绍
人物行走
人物的行走速度这边分成水平方向(X轴)和竖直方向(Y轴),水平方向的速度要考虑加速度和摩擦力,竖直方向的速度要考虑重力加速度。
水平方向:设定X轴向右走的速度为大于0,向左走的速度为小于0;
竖直方向:设定Y轴向下的速度为大于0,向上的速度为小于0。
游戏中的人物有下面几个主要的状态:
站立不动:水平方向速度为0,且竖直方向站在某个物体上。
向左或向右走:水平方向速度的绝对值大于0,且竖直方向站在某个物体上。
向上跳:竖直方向方向速度小于0,且上方没有碰到某个物体,同时需要玩家按住jump键。
向下降落:竖直方向方向速度大于0或者玩家没有按住jump键,且下方没有碰到某个物体。
向上跳和向下降落的状态判断可能一开始比较难理解,可以看后面的具体实现,目的是如果玩家长按jump键时,可以让人物跳的更高。
上面的判断是否站在某个物体上,或者是否碰到某个物体,就需要用到物体之间的碰撞检测。
碰撞检测
对于游戏中出现的每一样东西,比如砖块,箱子,水管,地面,还有人物都可以看成是一个独立的物体,所以每个物体类都继承了pygame的精灵类pg.sprite.Sprite,可以使用精灵类提供的碰撞检测函数来判断。
设置source\constants.py 中的变量DEBUG值为True,可以看到图1的游戏截图,比如最简单的地面,可以看成是一个长方形的物体。
下方红色的长方形物体就是地面(ground);
右边的几个红色小方块是阶梯(step);
左边空中的像墙一样的是砖块(brick);
带问号的是箱子(box)。
因为人物是站在地面上,且水平速度为0,所以当前的人物状态就是站立不动。
图1
游戏代码
游戏实现代码的github链接:https://github.com/marblexu/PythonSuperMario.git
这边是csdn的下载链接:https://download.csdn.net/download/marble_xu/11391533
代码介绍
人物行走代码
有一个单独的人物类,在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.x_vel -= self.x_accel
else:
self.x_vel = 0
self.state = c.STAND
else:
if self.x_vel 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
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?


微信扫码登录