目前为止,Player的站立、行走、跳跃都是动画了,只有跳板(即:Platform类)还是难看的矩形,这节我们把Platform也换成图片:
原来的Platform类长这个样子:
1 class Platform(pg.sprite.Sprite): 2 def __init__(self, x, y, w, h): 3 pg.sprite.Sprite.__init__(self) 4 self.image = pg.Surface((w, h)) 5 self.image.fill(GREEN) 6 self.rect = self.image.get_rect() 7 self.rect.x = x 8 self.rect.y = y
如果用图片,就不再需要w,h这个参数了(因为图片自带尺寸大小),所以变成下面这样:
1 class Platform(pg.sprite.Sprite): 2 def __init__(self, game, x, y): 3 pg.sprite.Sprite.__init__(self) 4 self.game = game 5 # 改成加载图片 6 images = [self.game.spritesheet.get_image("ground_grass_broken.png"), 7 self.game.spritesheet.get_image("ground_grass_small_broken.png")] 8 # 随机选一张 9 self.image = random.choice(images) 10 self.rect = self.image.get_rect() 11 self.rect.x = x 12 self.rect.y = y
相应的,最开始初始化的5块platform信息(settings.py)
1 # starting platform 2 PLATFORM_LIST = [(0, HEIGHT - 30, WIDTH, 30), 3 (WIDTH / 2 - 50, HEIGHT * 0.75, 100, 15), 4 (WIDTH * 0.12, HEIGHT * 0.5, 60, 15), 5 (WIDTH * 0.65, 200, 80, 10), 6 (WIDTH * 0.5, 100, 50, 10)]
调整成:
1 # starting platform 2 PLATFORM_LIST = [(5, HEIGHT - 35), 3 (WIDTH / 2 - 50, HEIGHT * 0.75), 4 (WIDTH * 0.12, HEIGHT * 0.5), 5 (WIDTH * 0.65, 200), 6 (WIDTH * 0.5, 100)]
同时,调整下Player出场时的位置,让它站在最底面的第1块板上:
class Player(pg.sprite.Sprite): def __init__(self, game): pg.sprite.Sprite.__init__(self) self.game = game ... self.rect = self.image.get_rect() # 初始化时,停在第一块platform上 self.rect.center = (35, HEIGHT - 35) self.pos = self.rect.center ...
main.py里的new函数,也做相应的调整:
def new(self): self.score = 0 ... for plat in PLATFORM_LIST: # 这里相应调整 p = Platform(self, *plat) ...
main.py中的update函数里,最后再调整一下:
1 def update(self): 2 self.all_sprites.update() 3 ... 4 5 while len(self.platforms) <= 5: 6 width = random.randrange(50, 100) 7 # 这里也做相应调整 8 p = Platform(self, random.randint(0, WIDTH - width), 9 random.randint(-70, -30)) 10 self.platforms.add(p) 11 ...
跑起来看看,基本效果出来了,难看的矩形终于没有了,但是仔细观察下,漏洞百出,比如下面这些:
问题1:跳板太靠右,边界跑到屏幕外了
修复方法:
检测下platform的right值,如果超出边界,向左挪一点
1 def new(self): 2 ... 3 4 for plat in PLATFORM_LIST: 5 p = Platform(self, *plat) 6 # 如果platform位置太靠右,跑出屏幕外,校正位置 7 if p.rect.right >= WIDTH: 8 p.rect.centerx = p.rect.centerx - (p.rect.right - WIDTH) - 2 9 ...
问题二:platform把player实例给挡住了
类似photoshop的图层一样,pygame里也有layer的概念,最后绘制的对象,默认在最上层
修复方法:main.py的draw函数,在最后,强制再绘制一次player(tips: 其实有更好的办法,利用图层概念,可参考part17部分)
1 def draw(self): 2 self.screen.fill(LIGHT_BLUE) 3 self.all_sprites.draw(self.screen) 4 self.debug() 5 self.draw_text(str(self.score), 22, WHITE, WIDTH / 2, 15) 6 # 强制把player放在最上层 7 self.screen.blit(self.player.image, self.player.rect) 8 pg.display.update()
问题三:跳板叠在一起
解决方法:
思路:随机生成的新跳板,先不急着加入self.platforms,而是运用碰撞检测原理,与现有跳板做碰撞检测(叠在一起,肯定就碰撞上了),如果碰撞了,就扔掉(pygame下一帧会重新生成,如此循环,直到满足条件的跳板加入)
1 def update(self): 2 self.all_sprites.update() 3 ... 4 # 跳板数<5,且player未跌落到屏幕外(否则player跌到屏幕外,仍在不停做碰撞检测,性能开销极大,会把程序卡死) 5 while len(self.platforms) <= 5 and self.player.rect.bottom < HEIGHT: 6 width = random.randrange(50, 100) 7 # 这里也做相应调整 8 p = Platform(self, random.randint(0, WIDTH - width), 9 random.randint(-70, -30)) 10 if p.rect.right >= WIDTH: 11 p.rect.centerx = p.rect.centerx - (p.rect.right - WIDTH) - 2 12 self.all_sprites.add(p) 13 # 防止二个plat平台叠在一起(原理:用待加入的plat与其它platform实例做碰撞检测,如果碰撞了,则扔掉) 14 hits = pg.sprite.spritecollideany(p, self.platforms) 15 if hits: 16 p.kill() 17 else: 18 self.platforms.add(p)
问题四: player已经超过了屏幕顶端,但是屏幕并没有向上滚动,这样玩家就无法看到头顶的新跳板。
解决办法:
先来分析下main.py中update函数中的滚动处理
if self.player.rect.top < HEIGHT / 4: self.player.pos.y += abs(self.player.vel.y) for plat in self.platforms: plat.rect.top += abs(self.player.vel.y) if plat.rect.top > HEIGHT: ...
如果player的y轴速度为0,abs函数算出来的值为0,所以跳板与兔子的y坐标值并不会动(也就是屏幕无法滚动),改进为下面这样:
1 def update(self): 2 self.all_sprites.update() 3 ... 4 if self.player.rect.top < HEIGHT / 4: 5 # 防止垂直方向速度为0时,无法滚动屏幕 6 self.player.pos.y += max(abs(self.player.vel.y), 2) 7 for plat in self.platforms: 8 plat.rect.top += max(abs(self.player.vel.y), 2) 9 if plat.rect.top > HEIGHT: 10 ...
修复了上面这一堆bug后,再来运行下:
源码: https://github.com/yjmyzz/kids-can-code/tree/master/part_12