阅读 108

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

前言

通过前面九篇文章的编写,「跳跳兔」游戏基本已经被编写出来了,本节在此基础上进一步完善它,并添加上云彩背景。

添加云彩背景

添加云彩背景的大致步骤如下。

  • 1.编写云彩类
  • 2.载入云彩图片
  • 3.随机生成云彩
  • 4.云彩同步移动

一步步来编写,首先是创建云彩类,代码如下。

# sprites.py

class Cloud(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = CLOUD_LAYER
        self.groups = game.all_sprites, game.clouds
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = random.choice(self.game.cloud_images)
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        # 随机出现位置
        scale = random.randrange(50, 101) / 100
        self.image = pg.transform.scale(self.image, (int(self.rect.width * scale),
                                                     int(self.rect.height * scale)))
        self.rect.x = random.randrange(WIDTH - self.rect.width)
        self.rect.y = random.randrange(-500, -50)

    def update(self):
        # 云朵大于2倍高度,就被消除
        if self.rect.top > HEIGHT * 2:
            self.kill()
复制代码

代码内容与此前内容非常类似,不再详细分析。

但你仔细观察,你会发现,Cloud类的__init__()方法中创建了 self._layer,并通过如下形式将其加入到相应的groups中。

self.groups = game.all_sprites, game.clouds
pg.sprite.Sprite.__init__(self, self.groups)
复制代码

这是一个优化点,后文再讨论。

构建了Cloud类后,接着要做的就是载入图片、随机生成以及同步移动了,轻车熟路。

# main.py/Game

    def load_data(self): # 加载数据
        # ... 省略无关代码
        # 加载云彩图片
        self.cloud_images = []
        for i in range(1, 4):
            self.cloud_images.append(pg.image.load(os.path.join(img_dir, 'cloud{}.png'.format(i))).convert())
        
    def new(self):
        self.score = 0
        # ... 省略无关代码
        # 创建云彩
        for i in range(8):
            c = Cloud(self)
            c.rect.y += 500
        self.run()
        
    def update(self):
         # 玩家到达游戏框 1/4 处时(注意,游戏框,头部为0,底部为游戏框长度,到到游戏框的1/4处,表示已经到达了顶部一部分了)
        if self.player.rect.top <= HEIGHT / 4:
            # 玩家位置移动(往下移动)
            self.player.pos.y += max(abs(self.player.vel.y), 2)
            # 随机生成新云朵
            if random.randrange(100) < 10:
                Cloud(self)
            # 云彩同步移动
            for cloud in self.clouds:
                cloud.rect.y += max(abs(self.player.vel.y / 2), 2)
            # 敌人位置同步往下移动
            for mob in self.mobs:
                mob.rect.y += max(abs(self.player.vel.y), 2)
            # 平台在游戏框外时,将其注销,避免资源浪费
            for plat in self.platforms:
                # 平台移动位置(往下移动,移动的距离与玩家相同,这样玩家才能依旧站立在原本的平台上)
                plat.rect.y += max(abs(self.player.vel.y), 2)
                if plat.rect.top >= HEIGHT: 
                    plat.kill()
                    # 分数增加 - 平台销毁,分数相加
                    self.score += 10
            
复制代码

woo~,搞定,如果有疑惑,可以拉下github代码对照着看。

优化

云彩类添加完了,接着来进行一些优化。

首先,对碰撞检测的优化,如果你仔细观察,你会发现,玩家对象与敌人对象的本体还没有接触到,就触发了碰撞检测,游戏就结束了,造成这种现象的原因是,玩家也好、敌人也好,游戏中的任何元素都是一个对应大小的长方形,碰撞检测默认形式就是对这两个方块进行检测,此时两个元素本身可能没有接触,但对应的方块接触到了,所以触发了碰撞检测。

为了避免这种情况,可以利用pygame的蒙版机制mask,为Player、Mob都创建相应的蒙版,具体做法如下。

# sprites.py

class Player(pg.sprite.Sprite):
    def animate(self):
        # ... 省略无关代码
        self.mask = pg.mask.from_surface(self.image) # 创建蒙版

class Mob(pg.sprite.Sprite):
    
    def update(self):
        # ... 省略无关代码
        self.rect = self.image.get_rect()
        self.mask = pg.mask.from_surface(self.image) # 创建蒙版
        self.rect.center = center
        # ... 省略无关代码
复制代码

检测时,加上pygame.sprite.collide_mask回调则可

def update(self):
        # ... 省略无关代码
        # 碰撞检测 - 如果碰撞到了敌人,游戏结束
        mob_hits = pg.sprite.spritecollide(self.player, self.mobs, False, pg.sprite.collide_mask)
        if mob_hits:
            self.playing = False
复制代码

此外还有个需要优化的问题,就是元素图层关系,增加云彩对象后,图层关系的问题显得明显,云彩作为背景却会遮挡玩家对象、敌人对象、平台对象等,这是不合理的,不同元素应该在不同图层,从而合理的显示出来。

首先定义好不同元素要出现的图层。

# settings.py

# 不同元素在不同层
PLAYER_LAYER = 2 # 玩家
PLATFORM_LAYER = 1 # 平台
POW_LAYER = 1 # 道具
MOB_LAYER = 2 # 敌人
CLOUD_LAYER = 0 # 云
复制代码

然后为不同的元素对象都添加上设置图层的逻辑

#Sprite.py

class Player(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = PLAYER_LAYER # 对应的图层
        self.groups = game.all_sprites # 所在的组
        pg.sprite.Sprite.__init__(self, self.groups) # 添加、实例化
        # ... 省略无关代码

class Platform(pg.sprite.Sprite):
    def __init__(self, game, x, y):
        self._layer = PLATFORM_LAYER # 对应的图层
        self.groups = game.all_sprites, game.platforms # 所在的组
        pg.sprite.Sprite.__init__(self, self.groups) # 添加、实例化
        # ... 省略无关代码

class Pow(pg.sprite.Sprite):
    def __init__(self, game, plat):
        self._layer = POW_LAYER
        self.groups = game.all_sprites, game.powerups
        pg.sprite.Sprite.__init__(self, self.groups)
        # ... 省略无关代码
        
class Mob(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = MOB_LAYER
        self.groups = game.all_sprites, game.mobs
        pg.sprite.Sprite.__init__(self, self.groups)
        # ... 省略无关代码
        
class Cloud(pg.sprite.Sprite):
    def __init__(self, game):
        self._layer = CLOUD_LAYER
        self.groups = game.all_sprites, game.clouds
        pg.sprite.Sprite.__init__(self, self.groups)
        # ... 省略无关代码
复制代码

优化后,再修改一个Game类的new()方法,使用pygame.sprite.LayeredUpdates()来实例化all_sprites对象。

LayeredUpdates(分层更新)是一个精灵组,它可以处理图层并顺序绘制元素。

# main.py

class Game:
    def new(self):
        self.score = 0
        # self.all_sprites = pg.sprite.Group()
        # 层次添加,避免元素重叠显示(如背景云遮挡住平台与玩家)
        self.all_sprites = pg.sprite.LayeredUpdates()
        self.platforms = pg.sprite.Group()
        self.powerups = pg.sprite.Group() # 急速飞跃道具
        self.mobs = pg.sprite.Group() # 敌人对象
        self.clouds = pg.sprite.Group() # 云彩对象
        self.player = Player(self)
        self.all_sprites.add(self.player)
        for plat in PLATFORM_LIST:
            p = Platform(self, *plat)
            # self.all_sprites.add(p)
            # self.platforms.add(p)
        self.mob_timer = 0
        # 游戏的背景音乐
        pg.mixer.music.load(os.path.join(self.snd_dir, 'Happy Tune.ogg'))
        # 创建云彩
        for i in range(8):
            c = Cloud(self)
            c.rect.y += 500
        self.run()
复制代码

最终效果如下。

「跳跳兔」至此开发完啦,Pygame系列的文章也暂时告一段落啦。

「跳跳兔」代码github:github.com/ayuLiao/jum…

Pygame还有很多有趣的功能在「跳跳兔」游戏中并没有体现出来。

正如本系列第一篇文章所说,这些文章只是学习笔记,此外还有下面两个游戏的学习笔记,一个是打飞机、一个是RPG打僵尸游戏。

如果大家感兴趣,记得告诉我,我才有动力继续分享,后面按个人计划应该会以漫画形式分享算法、计算机基础方面的东西,希望喜欢。

最后,再次声明一下,学习内容来自:kidscancode.org/,游戏并不是自主原创的…

如果文章对你有所帮助,请点「在看」给作者一点鼓励,叩谢豪恩。

关注下面的标签,发现更多相似文章
评论