上一篇讲解敌机、补给包、Boss的生成及控制。本篇就重点讲解碰撞检测原理和实现。
碰撞检测原理
图片在程序中都是矩形,我们看到的不规则形状,只是透明效果。所以碰撞实际就是两个矩形重叠。
以图中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)