这节研究下跳跃如何做得更自然,先看看之前的跳跃有什么问题,我们把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. ElementLayer support not implemented for native rendering. Layer ID:

    在 arcgis runtime for wpf 添加标注闪烁动画时(实现方法参考:http://blog.csdn.net/arcgisserver_book/article/details/805 ...

  2. Vue全局API总结

    1.extend用于创建一个子类Vue,用$mount来挂载 <body> <div id="app"></div> <script> ...

  3. python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性

    python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性 inspect import inspect def fun(): pass inspect.ism ...

  4. 用greenlet实现协程消费者生产者

    代码: from greenlet import greenlet import random def pro(): 生产者 while True: item = random.randint(0,9 ...

  5. git合并

    git 里合并了两个分支以后,是不是两个分支的内容就完全一样了? 不是.看合并到哪个分支,这个分支有两个分支所有的内容.另外一个分支不变. 合并操作( merge )对当前所在分支产生影响. 合并分支 ...

  6. HDU1029 Ignatius and the Princess IV (水题)

    <题目链接> 题目大意:给你一段序列,问你在这个序列中出现次数至少为 (n+1)/2 的数是哪个. 解题分析: 本题是一道水题,如果用map来做的话,就非常简单,但是另一个做法还比较巧妙. ...

  7. UVa-156 Ananagrams 反片语【map】【vector】

    题目链接:https://vjudge.net/contest/211547#problem/D 题目大意: 输入一些单词,找出所有满足以下条件的单词:该单词不能通过字母重排,得到输入文本中的另外一些 ...

  8. CUDA版Grabcut的实现

    在上次用 CUDA实现导向滤波 后,想着导向滤波能以很小的mask还原高分辨率下的边缘,能不能搞点事情出来,当时正好在研究Darknet框架,然后又看到grabcut算法,用opencv试了下,感觉效 ...

  9. Serializers 序列化组件

    Serializers 序列化组件   为什么要用序列化组件 当我们做前后端分离的项目~~我们前后端交互一般都选择JSON数据格式,JSON是一个轻量级的数据交互格式. 那么我们给前端数据的时候都要转 ...

  10. AGC 027B.Garbage Collector(贪心)

    题目链接 \(Description\) 坐标轴正半轴上有\(n\)个垃圾,位置分别是\(x_i\).在原点处有一个垃圾桶.一个机器人要从原点出发,将所有垃圾带到垃圾桶(原点)处. 机器人可以在坐标轴 ...