飞机大战(pygame)开发实录七

上一篇讲解敌机、补给包、Boss的生成及控制。本篇就重点讲解碰撞检测原理和实现。


碰撞检测原理

图片在程序中都是矩形,我们看到的不规则形状,只是透明效果。所以碰撞实际就是两个矩形重叠。

碰撞检测原理为矩形重叠
5种重叠

以图中5种重叠为例,重叠部分的矩形坐标都会满足

minX = max(minX1, minX2)
minY = max(minY1, minY2)
maxX = min(maxX1, maxX2)
maxY = min(maxY1, maxY2)

所以只要要判断这个矩形成立,即重叠。


矩形碰撞测试

def CollideTest(postion1, postion2): #碰撞检测
    minX1, minY1,maxX1, maxY1  = postion1
    minX2, minY2,maxX2, maxY2  = postion2
    minX = max(minX1, minX2)
    minY = max(minY1, minY2)
    maxX = min(maxX1, maxX2)
    maxY = min(maxY1, maxY2)
    if (minX < maxX) and (minY < maxY):
        return True
    else:
        return False

有了这个函数,我们只要把需要检测的两个对象的坐标(minX, minY,maxX, maxY )传入函数就可以了。
minX, minY 就是对象的x,y属性,maxX, maxY 可以通过x,y和w,h属性演算获得。

图片坐标获取

然后把对象分成 敌机、敌机子弹、补给包 与 玩家飞机玩家飞机子弹与敌机 两组,进行检测。
这里,玩家飞机做了一个特殊处理。把机头和机身分成1,2两个区域来做碰撞测试,这样3,4区域不会参与碰撞检测。

玩家飞机碰撞检测

碰撞测试

def IsCollide(hero, enemyplanes, Bullets, EnemyBullets, Rewards):
    if hero.live > 0 :
        # 敌机、敌机子弹、补给包 与 玩家飞机的 碰撞检测
        enemyObj = [enemyplanes, EnemyBullets, Rewards]
        for objs in enemyObj:
            for i in range(len(objs) - 1, -1, -1):
                postion1 = (objs[i].x, objs[i].y, objs[i].x + objs[i].w, objs[i].y + objs[i].h)         #(x1,y1,x2,y2)定位矩形区域
                postion2 = (hero.x + hero.w / 3, hero.y, hero.x + hero.w * 2 / 3, hero.y + hero.h / 4)  #机头部分
                postion3 = (hero.x, hero.y + hero.h / 4, hero.x + hero.w, hero.y + hero.h)              # 机身部分
                if CollideTest(postion1, postion2) or CollideTest(postion1, postion3):
                    PlaySound(Sound_Hit)
                    if isinstance(objs[i],EnemyBullet): #当前判断的是敌机子弹,
                        del(objs[i])
                        if hero.invincible == False:
                            hero.hit = True
                    elif isinstance(objs[i],EnemyPlane): #当前判断的是敌机
                        objs[i].hit = True
                        if hero.invincible == False:
                            hero.hit = True
                    elif isinstance(objs[i],RewardGoods): #当前判断的是补给包
                        hero.bulletType = objs[i].RewardItem
                        del(objs[i])

        # 子弹和敌机碰撞检测
        for i in range(len(Bullets) - 1,-1, -1):
            for k in range(len(enemyplanes) -1,-1,-1):
                postion1 = (Bullets[i].x,Bullets[i].y,Bullets[i].x+Bullets[i].w,Bullets[i].y+Bullets[i].h)
                postion2 = (enemyplanes[k].x, enemyplanes[k].y, enemyplanes[k].x + enemyplanes[k].w, enemyplanes[k].y + enemyplanes[k].h)
                if CollideTest(postion1, postion2):
                    del(Bullets[i])
                    enemyplanes[k].hit = True
                    break

带补给包的敌机,被摧毁会刷新补给包。需要先创建补给包的类 RewardGoods

class RewardGoods(Base):
    def __init__(self, pygame_screen, postion, image_name, RewardItem):
        Base.__init__(self, pygame_screen, postion, image_name)
        self.RewardItem = RewardItem
    def move(self):
        self.y += 1
    def display(self):
        self.screen.blit(self.image, (self.x, self.y))

更新Main()

def Main():
    pygame.init()
    scores = 0
    screen = pygame.display.set_mode((480, 852), 0, 32)
    # 添加背景
    Bg = BackGround(screen, Image_Background, 'dynamic')
    bullets = []
    rewards = []
    #添加测试敌机
    enemyBullets = []
    enemyPlanes = []
    enemyPlanes.append(EnemyPlane(screen,enemyBullets))
    enemyPlanes.append(BossPlane(screen,enemyBullets))
    enemyPlanes.append(RewardPlane(screen, enemyBullets,'shotbullet'))
    hero = HeroPlane(screen, bullets)

    while True:
        Bg.display()
        hero.move()  #生成飞机移动后位置
        hero.bulletCoolDown()  # 玩家飞机子弹冷却
        hero.fire()        # 玩家飞机发射子弹
        hero.display()        #绘制玩家飞机
        # 在列表中清除被消灭的敌机
        for i in range(len(enemyPlanes) - 1, -1, -1):
            if enemyPlanes[i].live == 0 and enemyPlanes[i].hit == False:
                if enemyPlanes[i].reward == "shotbullet":  # 敌机如果附带奖励
                    rewards.append(RewardGoods(screen, (enemyPlanes[i].x, enemyPlanes[i].y), Image_Reward2, "shotbullet"))
                elif enemyPlanes[i].reward == "doublebullet":  # 敌机如果附带奖励
                    rewards.append(RewardGoods(screen, (enemyPlanes[i].x, enemyPlanes[i].y), Image_Reward1, "doublebullet"))
                scores += enemyPlanes[i].score
                del (enemyPlanes[i])

        # 更新敌机状态
        for enemy in enemyPlanes:
            enemy.bulletCoolDown()
            enemy.move()
            enemy.fire()
            enemy.display()

        # 刷新奖励补给包
        for Reward in rewards:
            Reward.move()
            Reward.display()
        # IsOverBound(Bullets, EnemyBullets, enemyplanes, Rewards)
        IsOverBound(bullets,enemyBullets,enemyPlanes)
        # 碰撞检测
        IsCollide(hero, enemyPlanes, bullets, enemyBullets, rewards)
        pygame.display.update()
        MainControl(hero, screen)
运行效果

本篇就重点讲解碰撞检测原理和实现,至此游戏主体部分也完成了,下一篇开始逐步完善游戏,首先是增加背景和音效。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容