0%

利用pygame开发一款游戏:「跳跳兔」(九)

前言

在上一节中,我们对「跳跳兔」进行了简单的优化,然后为游戏中不同的状态添加不同的音乐。

这一节,为游戏添加道具与敌人,触碰到道具,玩家可以急速飞跃,触碰到敌人,玩家将会死亡,游戏结束

添加道具

先整理一下添加道具其整体思路。

  • 1.加载道具图片
  • 2.加载触发道具时的音效
  • 3.让道具随机出现到平台上
  • 4.碰撞检测 - 触碰到道具后会有的效果

想要的效果,道具随机出现在不同的平台上,玩家触碰到,会飞速向上飞跃。

一步步来实现。

首先构建一个新的类来表示这个道具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# sprites.py

class Pow(pg.sprite.Sprite):
def __init__(self, game, plat):
self.groups = game.all_sprites, game.powerups
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game # 整个游戏对象
self.plat = plat # 平台对象
self.type = random.choice(['boost'])
# 加载道具图片
self.image = self.game.spritesheet.get_image(820, 1805, 71, 70)
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
# 道具出现在平台中间的上方
self.rect.centerx = self.plat.rect.centerx
self.rect.bottom = self.plat.rect.top - 5

代码关键部分有相应的注释,不再详细分析。

然后在平台实例化时,随机实例化道具对象。

1
2
3
4
5
6
7
class Platform(pg.sprite.Sprite):
def __init__(self, game, x, y):
#... 省略无关代码

# 随机在平台上初始化道具
if random.randrange(100) < POW_SPAWN_PCT:
Pow(self.game, self)

这样,道具实例化就完成了。

接着添加音效。

1
2
3
4
5
6
7
8
9
def load_data(self): # 加载数据
# ... 省略无关代码

# 加载音乐
self.snd_dir = os.path.join(self.dir, 'snd')
# 跳跃时音效
self.jump_sound = pg.mixer.Sound(os.path.join(self.snd_dir, 'Jump33.wav'))
# 使用道具时音效
self.boost_sound = pg.mixer.Sound(os.path.join(self.snd_dir, 'Boost16.wav'))

至此,只剩道具碰撞检测逻辑未完成了。来搞一搞

1
2
3
4
5
6
7
8
9
10
11
12
# main.py/Game

def update(self):
# ... 省略无关代码
# 碰撞检测 - 玩家碰撞到急速飞跃道具
pow_hits = pg.sprite.spritecollide(self.player, self.powerups, True)
for pow in pow_hits:
# 道具类型 - 不同道具可以实现不同的效果
if pow.type == 'boost':
self.boost_sound.play() # 播放相应的音效
self.player.vel.y = -BOOST_POWER # 快递移动的距离
self.player.jumping = False # 此时不为跳跃状态

此时如果直接运行,会发现,玩家移动时,道具也会同步移动,原因是这部分代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# main.py/Game

def update(self):
# 调用所有元素的update()方法
self.all_sprites.update()

# ... 省略无关代码

# 玩家到达游戏框 1/4 处时(注意,游戏框,头部为0,底部为游戏框长度,到到游戏框的1/4处,表示已经到达了顶部一部分了)
if self.player.rect.top <= HEIGHT / 4:
# 玩家位置移动(往下移动)
self.player.pos.y += abs(self.player.vel.y)
# 平台在游戏框外时,将其注销,避免资源浪费
for plat in self.platforms:
# 平台移动位置(往下移动,移动的距离与玩家相同,这样玩家才能依旧站立在原本的平台上)
plat.rect.y += abs(self.player.vel.y)
if plat.rect.top >= HEIGHT:
plat.kill()
# 分数增加 - 平台销毁,分数相加
self.score += 10

此时为了避免道具同步移动,在Pow类中创建update()方法,实现如下逻辑。

1
2
3
4
5
6
7
8
9
10
11
# 道具对象
class Pow(pg.sprite.Sprite):
def __init__(self, game, plat):
# ... 省略无关代码

def update(self):
# 更新时,避免道具一同移动
self.rect.bottom = self.plat.rect.top - 5
# 如果道具对应的平台已经被消除,那么道具也要被消除
if not self.game.platforms.has(self.plat):
self.kill() # 消除道具

最终效果如下。

添加敌人

在实现前,依旧先整理一下实现的整体逻辑。

  • 1.构建敌人类
  • 2.敌人移动效果
  • 3.敌人随机出现效果
  • 4.敌人碰撞检测
  • 5.敌人不与跳跳兔同步移动

首先来构建敌人类,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# sprites.py

class Mob(pg.sprite.Sprite):
def __init__(self, game):
self.groups = game.all_sprites, game.mobs
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
# 加载不同状态的图片
self.image_up = self.game.spritesheet.get_image(566, 510, 122, 139)
self.image_up.set_colorkey(BLACK)
self.image_down = self.game.spritesheet.get_image(568, 1534, 122, 135)
self.image_down.set_colorkey(BLACK)
self.image = self.image_up # 默认为向上的图片
self.rect = self.image.get_rect()
# x轴中心位置随机选择
self.rect.centerx = random.choice([-100, WIDTH + 100])
# 随机x轴速度
self.vx = random.randrange(1, 4)
if self.rect.centerx > WIDTH:
self.vx *= -1
# 随机敌人y轴位置
self.rect.y = random.randrange(HEIGHT / 2)
self.vy = 0 # y轴速度默认为0
self.dy = 0.5

__init__()方法中,为敌人添加了不同状态的两种图片并随机敌人初始centerx、x轴速度与y轴位置。

接着实现敌人移动效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# sprites.py

class Mob(pg.sprite.Sprite):

def __init__(self, game):
# ... 省略

def update(self):
self.rect.x += self.vx # x轴位置移动
self.vy += self.dy
# 实现上下抖动移动的效果
if self.vy > 3 or self.vy < -3:
self.dy *= -1
center = self.rect.center
# 上下移动方向不同,使用不同的图片
if self.dy < 0:
self.image = self.image_up
else:
self.image = self.image_down
self.rect = self.image.get_rect()
self.rect.center = center
# y轴具体的移动
self.rect.y += self.vy
# 超过屏幕范围,删除敌人
if self.rect.left > WIDTH + 100 or self.rect.right < -100:
self.kill()

看注释就可明白代码的含义,不再详细分析。

接着实现产生敌人与碰撞检测相关逻辑,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# main.py/Game

def new(self):
self.score = 0
# ... 省略无关代码
self.powerups = pg.sprite.Group() # 急速飞跃道具
self.mobs = pg.sprite.Group() # 敌人对象
# ... 省略无关代码
self.mob_timer = 0

def update(self):
self.all_sprites.update()
# 产生敌人
now = pg.time.get_ticks()
# 通过时间间隔来判断是否要产生敌人
if now - self.mob_timer > 5000 + random.choice([-1000, -500, 0, 500, 1000]):
self.mob_timer = now
Mob(self)
# 碰撞检测 - 如果碰撞到了敌人,游戏结束
mob_hits = pg.sprite.spritecollide(self.player, self.mobs, False)
if mob_hits:
self.playing = False

通过时间间隔的形式随机产生敌人,至于碰撞检测…一样的逻辑。

最后,为了避免敌人元素跟随跳跳兔一同移动,需要添加如下逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def update(self):
# 玩家到达游戏框 1/4 处时(注意,游戏框,头部为0,底部为游戏框长度,到到游戏框的1/4处,表示已经到达了顶部一部分了)
if self.player.rect.top <= HEIGHT / 4:
# 玩家位置移动(往下移动)
self.player.pos.y += abs(self.player.vel.y)
# 平台在游戏框外时,将其注销,避免资源浪费
for plat in self.platforms:
# 平台移动位置(往下移动,移动的距离与玩家相同,这样玩家才能依旧站立在原本的平台上)
plat.rect.y += abs(self.player.vel.y)
if plat.rect.top >= HEIGHT:
plat.kill()
# 分数增加 - 平台销毁,分数相加
self.score += 10
# 敌人位置同步往下移动
for mob in self.mobs:
mob.rect.y += max(abs(self.player.vel.y), 2)

最终效果如下。

在本节中,我们对「跳跳兔」做了道具与敌人的添加,增加了「跳跳兔游戏的趣味性」。

因为考虑到篇幅,文中没有给出完整的代码,但为了方便大家理解,我将相应的代码上传到了github,图片资源与音乐资源也在github上。

https://github.com/ayuLiao/jumprabbit

如果文章对你有帮助或你觉得有点意思,点击「在看」支持作者一波。