这节研究下跳跃如何做得更自然,先看看之前的跳跃有什么问题,我们把settings.py里的初始化参数调整下:

 # starting platform
# PLATFORM_LIST = [(5, HEIGHT - 35),
# (WIDTH / 2 - 50, HEIGHT * 0.75),
# (WIDTH * 0.12, HEIGHT * 0.5),
# (WIDTH * 0.65, 200),
# (WIDTH * 0.5, 100)] PLATFORM_LIST = [(15, HEIGHT - 35),
(55, HEIGHT - 140),
(5, HEIGHT - 215),
(WIDTH * 0.70, 180),
(WIDTH * 0.5, 100)]

同时,把Platform类微调一下,只加载长的跳板:

 class Platform(pg.sprite.Sprite):
def __init__(self, game, x, y):
pg.sprite.Sprite.__init__(self)
self.game = game
images = [self.game.spritesheet.get_image("ground_grass_broken.png"),
self.game.spritesheet.get_image("ground_grass_small_broken.png")]
# self.image = random.choice(images)
# 临时改成只使用长的跳板
self.image = images[0]
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y

仔细观察一下,有二个问题:

1. (当跳板上下间隔较小时)player越过第2块跳板(从下向上数,初始时,站着的那块为1),直接蹦到第3块上去了,有点不太自然,如果头顶有板的话,最好是落在最低的那块上

2.从第3块,向下落到第2块时,继续向左走,理论上应该落到第1块板上,但是无论如何,总是会被第3块板自动吸上去。

原因:连续碰到多个跳板时,碰撞检测返回的是一个被碰到的跳板数组,hits[0]返回的是最高的那块,所以总是被吸上去。

改进思路:找出最低那块,后面的就好处理了。

    def update(self):
self.all_sprites.update()
if self.player.vel.y > 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
# 当player向上同时碰撞到多个跳板(注:跳板之间挨得很近时,容易出现这种情况)
# 找出最低的那块,让player落上最低的跳板上
lowest = hits[0]
for hit in hits:
if hit.rect.bottom > lowest.rect.bottom:
lowest = hit
if self.player.pos.y < lowest.rect.bottom:
self.player.pos.y = lowest.rect.top
self.player.vel.y = 0
...

改进后的效果:

搞定这个,还有一个小问题:

当player走到跳板边缘时,实际上确实发生了碰撞(从垂直方向上看,player的身体与跳板有重叠,即碰撞),但从视觉上看,双脚已经离开跳板了,应该向下掉,看上去不太真实。

改进办法:

发生碰撞时,对比player.centerx(角色的x轴中心点)与跳板的left/right值,只有x轴中心点未离开跳板时,才认为真正发生了碰撞。

仍然是修改刚才的碰撞检测代码:(注:具体实现时,下面的代码在两侧保留了5px的余量,大家可以调整下这个值,以控制检测的灵敏度)

    def update(self):
self.all_sprites.update()
if self.player.vel.y > 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
# 当player向上同时碰撞到多个跳板(注:跳板之间挨得很近时,容易出现这种情况)
# 找出最低的那块,让player落上最低的跳板上
lowest = hits[0]
for hit in hits:
if hit.rect.bottom > lowest.rect.bottom:
lowest = hit
if self.player.pos.y < lowest.rect.bottom:
# fix 走到跳板最边缘时,仍挂在半空中,不掉下去
if lowest.rect.right + 5 >= self.player.rect.centerx >= lowest.rect.left - 5:
self.player.pos.y = lowest.rect.top
self.player.vel.y = 0
...

效果:

最后一个可以改进的地方,玩过超级玛丽的大概还记得这么一个细节:跳跃时,如果空格键按得比较重,会跳得较高,反之如果轻轻按一下,马上松开,跳跃的高度相对就很少。

分析一下其中的原理,其实按键较重时,『按下的时间』相对轻轻一按马上抬起,会略长一点。所以,关键在于KEYUP事件,只要在该事件中,想办法快速终止跳跃,自然向上跳的高度就小。

在event事件中,先添加对KEYUP的响应:

     def events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.player.jump()
# 按键松开时,强行中断跳跃
if event.type == pg.KEYUP:
if event.key == pg.K_SPACE:
self.player.jump_cut()

然后在Player类中,新增jump_cut函数:

     def jump_cut(self):
if self.jumping:
# 给1个很小的正向速度,让其下降
self.vel.y = 1

如果觉得vel.y=1这样有点粗暴(相当于把上升直接骤变为下降),也可以改进成下面这样:

     def jump_cut(self):
if self.jumping:
if self.vel.y < -3:
self.vel.y = -3

即:如果上升过快(即向上跳的速度过大),则让它变成一个较小的速度-3px,这样从视觉上看运动过程要连贯一些。

此外,jump函数中,也要结合self.jumping标志位一起判断,同时要设置jumping标志位的值:

     def jump(self):
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
# 加入状态位判断
if hits and not self.jumping:
self.vel.y = -PLAYER_JUMP
if abs(self.vel.x) < 0.5:
self.jumping = True

另外,在下落停在档板上时,需要把jumping状态设置为False(main.py中的update函数里微调):

     def update(self):
self.all_sprites.update()
if self.player.vel.y > 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
lowest = hits[0]
for hit in hits:
if hit.rect.bottom > lowest.rect.bottom:
lowest = hit
if self.player.pos.y < lowest.rect.bottom:
if lowest.rect.right + 5 >= self.player.rect.centerx >= lowest.rect.left - 5:
self.player.pos.y = lowest.rect.top
self.player.vel.y = 0
# 停下后,修改状态位
self.player.jumping = False

效果如下:

源码:https://github.com/yjmyzz/kids-can-code/tree/master/part_13

pygame-KidsCanCode系列jumpy-part13-改进跳跃的更多相关文章

  1. redis 系列7 数据结构之跳跃表

    一.概述 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树 ...

  2. sitecore系列教程之改进Sitecore编辑体验的5个步骤

    Sitecore完全关注客户体验,在适当的时间为合适的人提供合适的体验.虽然没有人会不同意客户体验是王道,但我们仍然需要记住每天使用Sitecore的人们为客户带来惊人体验的体验. 我看到无数客户通过 ...

  3. redis 系列14 有序集合对象

    一. 有序集合概述 Redis 有序集合对象和集合对象一样也是string类型元素的集合,且不允许重复的成员.不同的是每个元素都会关联一个double类型的分数.redis正是通过分数来为集合中的成员 ...

  4. Python游戏编程(Pygame)

    安装Pygame pip install pygame C:\Users> pip install pygame Collecting pygame Downloading https://fi ...

  5. 【目录】redis 系列篇

    随笔分类 - redis 系列篇 redis 系列27 Cluster高可用 (2) 摘要: 一. ASK错误 集群上篇最后讲到,对于重新分片由redis-trib负责执行,关于该工具以后再介绍.在进 ...

  6. 深度学习与CV教程(12) | 目标检测 (两阶段,R-CNN系列)

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...

  7. 玩家福音:10款最佳Linux免费游戏

    “我能在Linux平台上游戏吗?”这类疑问正困扰游戏玩家,那么答案就是“快去Linux平台吧!”.开源组织一直以来坚持不懈为Linux操作系统开发不同类型的游戏,在Linux平台下的游戏完全不亚于其他 ...

  8. 开源玩家福利:十大Linux免费游戏

    假如当你考虑从Windows平台迁移至Linux平台时,“我能在Linux平台上游戏吗?”这类疑问正困扰着你,那么对此这有一个答案就是“快去Linux平台吧!”.感谢开源组织一直以来坚持不懈为Linu ...

  9. (开源项目)abattoir unity游戏

    (开源项目)abattoir unity游戏 欢迎各位的改进和提议! 名称: abattoir(角斗场) 版本: v1.0 作者: N-n-N(笔者) 简介: 添加娱乐(冲撞)模式和普通(一般)模式 ...

  10. 微软Visual Studio二十周年:VS2017于3月7日发布

    二十年前的今天,微软正式发布Visual Studio 97.如今二十年已经过去,微软宣布全新的Visual Studio 2017即将在美国当地时间3月7日正式发布. VS97是Visual Stu ...

随机推荐

  1. Python 命令模式和交互模式

    命令模式 在系统CMD命名模式下执行 命令执行到脚本所在目录 执行python Test.py 可直接一次执行完脚本里面所有的语句 交互模式下 一行一行执行

  2. Codeforces 436E Cardboard Box (看题解)

    Cardboard Box 贪了个半天贪不对, 我发现我根本就不会贪心. 我们先按b排序, 然后枚举选两颗心的b的最大值, 在这个之前的肯定都要选一个, 因为前面的要是一个都没选的话, 你可以把当前选 ...

  3. 3186Treats for the Cows(区间dp)

    题意:给一个数组v,每次可以取前面的或者后面的,第k次取的v[i]价值为v[i]*k,问总价值最大是多少. 区间dp. 区间dp可以不枚举len  直接枚举i和j即可  见代码 #include &l ...

  4. 039 在weblogic下部署jndi的多数据源

    这个问题,在公司遇到了,一直没有学,今天学了一下. 后续,还要实验一下,暂时粘贴一下一个不错的url:https://www.cnblogs.com/xdp-gacl/p/4201094.html

  5. VS2013 FFmpeg开发环境配置

    1.下载ffmpeg包(dll.include.lib)   https://ffmpeg.zeranoe.com/builds/         有3个版本:Static.Shared和Dev St ...

  6. Unable to load configuration. - action - file:/F:/apache-tomcat-8.0.30/webapps/test1Struts2/WEB-INF/classes/struts.xml:11:71

    Unable to load configuration. - action - file:/F:/apache-tomcat-8.0.30/webapps/test1Struts2/WEB-INF/ ...

  7. 最短路(bellman)-hdu2066

    题目链接:https://vjudge.net/problem/HDU-2066 题目描述: 代码实现: #include <cstdio> #include <cstring> ...

  8. 001.Kubernetes简介

    一 Kubernetes概述 Kubernetes是一个全新的基于容器技术的分布式架构领先方案.Kubernetes(k8s)是Google开源的容器集群管理系统(谷歌内部:Borg).在Docker ...

  9. 【C#】用委托(Delegate)的BeginInvoke和EndInvoke方法操作线程

    让我们首先了解下什么时候用到C#异步调用: .NET Framework 允许您C#异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的Begin ...

  10. GIL锁、进程池与线程池

    1.什么是GIL? 官方解释: ''' In CPython, the global interpreter lock, or GIL, is a mutex that prevents multip ...