Python游戏系列之六_击落敌机


  通过前面的代码,我们实现了我方飞机发射子弹,也实现了敌方飞机的随机出现,但实际运行效果是我方子弹和敌方飞机互相穿透,视对方如无物。

  因此,现在我们要来解决一个很重要的游戏问题:碰撞检测。


  一、碰撞检测

  碰撞检测是游戏中非常重要的部分。简单地说,碰撞检测,就是判断两个物体是否发生了碰撞。我方英雄一个大招,是否打中敌人了呢?大招有范围,如果敌人在范围内,就是打中。

  为了简化计算,通常把物体简化为矩形或圆形,这样在数学上比较好算。如果物体是不规则的,也可以把它分成几个矩形(或圆形)构成的子物体。

  在更精细一些的游戏中,有时会把主角分为头部、胸部、腹部、四肢等部位,因为每个部位受伤对人物的影响不同。


  pygame.sprite库中,有一些函数可以用于碰撞检测。参看文档:http://www.pygame.org/docs/ref/sprite.html(从最后往前看)

  例如:

  pygame.sprite.collide_rect():检测两个精灵是否碰撞,两个精灵视为矩形。

  pygame.sprite.collide_circle():检测两个精灵是否碰撞,两个精灵视为圆形。

  pygame.sprite.groupcollide():检测两个精灵组是否碰撞,依次判断精灵组A中的每一个精灵是否与精灵组B中的每一个精灵发生碰撞。


  groupcollide()这个函数正是我们要的,因为我方子弹有很多,是精灵组,敌方飞机也有很多,也是精灵组。

  让我们来认真研究一下这个函数。


  函数原型:

  groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict

  参数:

  group1:精灵组1。

  group2:精灵组2。

  dokill1:发生碰撞时,是否销毁精灵组1中的发生碰撞的精灵。

  dokill2:发生碰撞时,是否销毁精灵组2中的发生碰撞的精灵。

  collided:自定义的回调函数,你可以自己编写碰撞检测函数。碰撞检测函数的参数是两个精灵,返回值是True/False。

  返回值:

  返回一个字典,键是精灵组1中发生碰撞的精灵,值是精灵组2中与该精灵发生碰撞的精灵的列表。


  二、添加代码

  在变量定义部分,添加如下代码:

# 创建被击中的敌方飞机组
enemy_hit_group = pygame.sprite.Group()



  在游戏主循环中,添加如下代码:

# 检测我方子弹是否击中敌方飞机
enemy_hit_group.add(pygame.sprite.groupcollide(enemy_group, hero.bullets, True, True))



  这样,你就会看到我方子弹击中敌方飞机,然后两者同时消失的场面了。

  其实如果只是这样,根本不需要定义enemy_hit_group这个精灵组,直接调用pygame.sprite.groupcollide()函数即可,因为这个函数检测到碰撞时,会销毁发生碰撞的双方物体(因为我们把第3个参数和第4个参数都设成True了呀~~~)。

  之所以要定义这个精灵组,主要是想演绎出更震撼的爆炸效果,所以先记住要发生爆炸的敌方飞机,然后在敌方飞机的位置上依次播放爆炸图片,产生爆炸的效果。


  三、爆炸效果

  1. 首先截取爆炸图片:

基于Python的网页tokenpacket碰撞器_碰撞检测

  代码如下:

enemy_hit_surface = list()
enemy_hit_surface.append(shoot_img.subsurface(pygame.Rect(267, 347, 57, 43)))
enemy_hit_surface.append(shoot_img.subsurface(pygame.Rect(873, 697, 57, 43)))
enemy_hit_surface.append(shoot_img.subsurface(pygame.Rect(267, 296, 57, 43)))
enemy_hit_surface.append(shoot_img.subsurface(pygame.Rect(930, 697, 57, 43)))



  2. 在敌方飞机类中添加一个属性(构造方法中):

self.explode_index = 0



  3. 在游戏主循环的绘图部分添加如下代码:

# 绘制爆炸效果
    for enemy_hit in enemy_hit_group:
        screen.blit(enemy_hit_surface[enemy_hit.explode_index], enemy_hit.rect)
        if ticks % (ANIMATE_CYCLE // 2) == 0:
            if enemy_hit.explode_index < 3:
                enemy_hit.explode_index += 1
            else:
                enemy_hit_group.remove(enemy_hit)


  产生爆炸效果的原理是这样的:

  将几张图片保存在一个列表中;

  通过一个变量从列表中取图片;

  需要切换图片时,让此变量递增;

  当变量超过范围时,将敌方飞机从爆炸列表中移走。


  至此,华丽的爆炸效果就实现啦!