python飞机大战03-碰撞检测
1 碰撞
碰撞是游戏开发的基本部分。 碰撞检测意味着你要检测游戏世界中的一个对象是否正在触碰另一个对象。 碰撞反应决定了碰撞发生时你想要做什么——马里奥拿起硬币,子弹伤害敌人等等。
1.1 边框
请记住,pygame中的每个sprite都有一个 rect 属性定义其坐标及其大小。 rect 在pygame的对象格式为 [x, y, width, height] ,其中 x 和 y 表示矩形的左上角。
为了检测碰撞,需要查看玩家的 角色rect 并将其与每个怪物 rect 进行比较。现在可以通过循环遍历怪物并为执行此比较的每个人执行此操作:
在此图片中,您可以看到只有红色矩形1与大黑色矩形相撞。2在y中重叠,但在x轴不重叠;3在x轴上重叠,但不在y轴重叠 。为了使两个矩形重叠,它们的边界必须在每个轴上重叠。在代码中写这个:
# if mob.rect.right > player.rect.left and \ # 反斜杠表示续行
# mob.rect.left < player.rect.right and \
# mob.rect.bottom > player.rect.top and \
# mob.rect.top < player.rect.bottom:
# collide = True
spritecollide()
幸运的是,pygame通过使用一个内置函数 spritecollide() 来完成上述操作。
spritecollide()
参数:
- 要检查的玩家精灵
- 要比较的敌人组的名称
- dokill 参数(True / False)。dokill 参数设置是否应该在命中对象时删除该对象。
返回值:
spritecollide() 命令的返回值是被击中的精灵列表(记住,玩家可能一次与多个暴徒相撞)。
2 与玩家碰撞的怪物
将把这个命令添加到游戏循环的“更新”部分:
# Update # 游戏结束的话直接将running设为False即可
all_sprites.update()
# check to see id a mob hit the player
hits = pygame.sprite.spritecollide(player, mobs, False)
if hits:
running = False
将碰撞列表分配给变量 hits 。如果 hits 列表不为空,设置 running 为 False ,使得游戏结束。
3 回击
3.1 子弹精灵
添加一个新精灵:子弹。这将是按键时产生的精灵,出现在玩家精灵的顶部,并以相当高的速度向上移动。
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()
在 init() 子弹精灵的方法中,传递 x 和 y 值,以便可以告诉精灵在哪里出现。由于玩家精灵可以移动,设置为玩家射击时的位置。设置 speedy 为负值,以便它会向上移动。
最后,检查子弹是否已经脱离屏幕顶部,如果是,将其删除。
3.2 keypress事件
为了让事情变得简单,要做到这一点,每次玩家按下空格键时,都会发射子弹。需要将其添加到事件检查中:
# Process input(events) # 这是游戏主循环,通过变量running控制,如果需要
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
新代码检查 KEYDOWN 事件,如果有事件,则检查它是否是 K_SPACE 键。如果是的话,将运行玩家精灵的 shoot() 方法。
3.3 大量子弹
首先,需要添加一个新精灵组来保存所有子弹对象:
bullets = pygame.sprite.Group()
现在,可以在 Player 类中添加以下方法:
def shoot(self):
bullet = Bullet(self.rect.centerx,self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
shoot() 方法会产生一颗子弹,使用玩家的顶部中心作为生成点。然后将子弹添加到 all_sprites精灵组 (绘制和更新)。以及 bullets精灵组 (碰撞检查)。
3.3.1 子弹碰撞
现在需要检查子弹是否击中怪物。这里的区别是有多个子弹(在 bullets 组中)和多个小怪
(在 mobs 组中),所以不能用 spritecollide() ,因为它只比较一个精灵与一个组。将使
用 groupcollide() :
# Update # 游戏结束的话直接将running设为False即可
all_sprites.update()
# check to see if a bullet hit a mob
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
m = Mob()
all_sprites.add(m)
mobs.add(m)
该 groupcollide() 功能类似于 spritecollide() ,除了命名两个组进行比较,您将获得的是被击中的怪物字典,键为参数1中的其中一个,值为参数2中的其中一个。有两个 dokill 选项,每个选项对应一个组。
如果只是删除小怪,就会遇到一个问题:怪物用完了!所以做的是循环 hits ,对于销毁的每个怪物,将产生另一个新的怪物。
整合到一起
# Shmup game - part 3
# shmup.py
# Collisions and bullets
import pygame
import random
WIDTH = 480 # width of our game window
HEIGHT = 600 # height of our game window
FPS = 60 # 60 frames per second
# Colors(R,G,B),define color
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
YELLOW = (255,255,0)
# initialize pygame and create windw
pygame.init() # 启动pygame并初始化
pygame.mixer.init() # 声音初始化
screen = pygame.display.set_mode((WIDTH, HEIGHT)) # 游戏屏幕,按照在配置常量中设置的大小创建
pygame.display.set_caption("Shmup!")
icon = pygame.image.load("img/alien.ico")
pygame.display.set_icon(icon)
clock = pygame.time.Clock() # 创建一个时钟以便于确保游戏能以指定的FPS运行
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 40))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH/2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
def update(self):
# any code here will happen every time the game loop updates
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -8
if keystate[pygame.K_RIGHT]:
self.speedx = 8
self.rect.x += self.speedx
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
class Mob(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 40))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedx = random.randrange(-3, 3)
self.speedy = random.randrange(1, 8)
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.top > HEIGHT + 10 or self.rect.left < -50 or self.rect.right > WIDTH + 20:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()
all_sprites = pygame.sprite.Group()
mobs = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(8):
m = Mob()
all_sprites.add(m)
mobs.add(m)
# Game Loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input(events) # 这是游戏主循环,通过变量running控制,如果需要
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
# Update # 游戏结束的话直接将running设为False即可
all_sprites.update()
# check to see if a bullet hit a mob
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
m = Mob()
all_sprites.add(m)
mobs.add(m)
# check to see if a mob hit the player
hits = pygame.sprite.spritecollide(player, mobs, False)
if hits:
running = False
# Render(draw) # 现在还没有确定具体的代码,先用一些基本代码填充,后续再补充
screen.fill(BLACK)
all_sprites.draw(screen)
# *after* drawing everything,flip the display
pygame.display.flip()
pygame.quit()
项目代码可以查看我的github,网址为
alien-invasion