如何检测pygame中的碰撞?
Posted
技术标签:
【中文标题】如何检测pygame中的碰撞?【英文标题】:How do I detect collision in pygame? 【发布时间】:2015-06-20 20:25:01 【问题描述】:我使用下面的类制作了一个子弹列表和一个精灵列表。如何检测子弹是否与精灵碰撞,然后删除该精灵和子弹?
#Define the sprite class
class Sprite:
def __init__(self,x,y, name):
self.x=x
self.y=y
self.image = pygame.image.load(name)
self.rect = self.image.get_rect()
def render(self):
window.blit(self.image, (self.x,self.y))
# Define the bullet class to create bullets
class Bullet:
def __init__(self,x,y):
self.x = x + 23
self.y = y
self.bullet = pygame.image.load("user_bullet.BMP")
self.rect = self.bullet.get_rect()
def render(self):
window.blit(self.bullet, (self.x, self.y))
【问题讨论】:
我会注意到 pygame 中有一个 Sprite 类 - 我不确定在您的代码中重新定义它是一个好主意。此外,它们是否真的是目标(因为需要一个更好的词),因为精灵只是一个在屏幕上具有图形表示的对象(因此您的 Bullet 也是一个精灵)。 【参考方案1】:在 PyGame 中,可以使用pygame.Rect
对象来完成基本的碰撞检测。 Rect
对象提供了多种方法来检测对象之间的碰撞。请注意,即使在 Pong 游戏中,矩形对象与圆形对象(例如桨和球)的碰撞也可以通过两个矩形对象(桨叶和边界矩形)之间的碰撞来粗略检测。球。
一些例子:
pygame.Rect.collidepoint
:
测试一个点是否在矩形内
repl.it/@Rabbid76/PyGame-collidepoint
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(100, 100)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
point = pygame.mouse.get_pos()
collide = rect.collidepoint(point)
color = (255, 0, 0) if collide else (255, 255, 255)
window.fill(0)
pygame.draw.rect(window, color, rect)
pygame.display.flip()
pygame.quit()
exit()
pygame.Rect.colliderect
测试两个矩形是否重叠
另见How to detect collisions between two rectangular objects or images in pygame
repl.it/@Rabbid76/PyGame-colliderect
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
rect1 = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
rect2 = pygame.Rect(0, 0, 75, 75)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
rect2.center = pygame.mouse.get_pos()
collide = rect1.colliderect(rect2)
color = (255, 0, 0) if collide else (255, 255, 255)
window.fill(0)
pygame.draw.rect(window, color, rect1)
pygame.draw.rect(window, (0, 255, 0), rect2, 6, 1)
pygame.display.flip()
pygame.quit()
exit()
此外,pygame.Rect.collidelist
和pygame.Rect.collidelistall
可用于矩形与矩形列表之间的碰撞测试。 pygame.Rect.collidedict
和pygame.Rect.collidedictall
可用于矩形与矩形字典的碰撞测试。
pygame.sprite.Sprite
和pygame.sprite.Group
对象的碰撞,可以通过pygame.sprite.spritecollide()
、pygame.sprite.groupcollide()
或pygame.sprite.spritecollideany()
检测。使用这些方法时,可以通过collided
参数指定碰撞检测算法:
collided 参数是一个回调函数,用于计算两个精灵是否发生碰撞。
可能的collided
可调用对象是collide_rect
、collide_rect_ratio
、collide_circle
、collide_circle_ratio
、collide_mask
一些例子:
pygame.sprite.spritecollide()
repl.it/@Rabbid76/PyGame-spritecollide
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
sprite1 = pygame.sprite.Sprite()
sprite1.image = pygame.Surface((75, 75))
sprite1.image.fill((255, 0, 0))
sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
sprite2 = pygame.sprite.Sprite()
sprite2.image = pygame.Surface((75, 75))
sprite2.image.fill((0, 255, 0))
sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
all_group = pygame.sprite.Group([sprite2, sprite1])
test_group = pygame.sprite.Group(sprite2)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sprite1.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.spritecollide(sprite1, test_group, False)
window.fill(0)
all_group.draw(window)
for s in collide:
pygame.draw.rect(window, (255, 255, 255), s.rect, 5, 1)
pygame.display.flip()
pygame.quit()
exit()
有关与面具的碰撞,请参阅How can I made a collision mask? 或Pygame mask collision
另见Collision and Intersection
pygame.sprite.spritecollide()
/collide_circle
repl.it/@Rabbid76/PyGame-spritecollidecollidecircle
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
sprite1 = pygame.sprite.Sprite()
sprite1.image = pygame.Surface((80, 80), pygame.SRCALPHA)
pygame.draw.circle(sprite1.image, (255, 0, 0), (40, 40), 40)
sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(40, 40)
sprite2 = pygame.sprite.Sprite()
sprite2.image = pygame.Surface((80, 89), pygame.SRCALPHA)
pygame.draw.circle(sprite2.image, (0, 255, 0), (40, 40), 40)
sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80)
all_group = pygame.sprite.Group([sprite2, sprite1])
test_group = pygame.sprite.Group(sprite2)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sprite1.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.spritecollide(sprite1, test_group, False, pygame.sprite.collide_circle)
window.fill(0)
all_group.draw(window)
for s in collide:
pygame.draw.circle(window, (255, 255, 255), s.rect.center, s.rect.width // 2, 5)
pygame.display.flip()
pygame.quit()
exit()
这对您的代码意味着什么?
pygame.Surface.get_rect.get_rect()
返回一个具有 Surface 对象大小的矩形,该矩形始终从 (0, 0) 开始,因为 Surface 对象没有位置。矩形的位置可以由关键字参数指定。例如,可以使用关键字参数center
指定矩形的中心。这些关键字参数在返回之前应用于pygame.Rect
的属性(有关关键字参数的完整列表,请参见pygame.Rect
)。
见*Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
您根本不需要Sprite
和Bullet
的x
和y
属性。请改用rect
属性的位置:
#Define the sprite class
class Sprite:
def __init__(self, x, y, name):
self.image = pygame.image.load(name)
self.rect = self.image.get_rect(topleft = (x, y))
def render(self):
window.blit(self.image, self.rect)
# Define the bullet class to create bullets
class Bullet:
def __init__(self, x, y):
self.bullet = pygame.image.load("user_bullet.BMP")
self.rect = self.bullet.get_rect(topleft = (x + 23, y))
def render(self):
window.blit(self.bullet, self.rect)
使用pygame.Rect.colliderect()
检测Sprite
和 Bullet
实例之间的冲突。
见How to detect collisions between two rectangular objects or images in pygame:
my_sprite = Sprite(sx, sy, name)
my_bullet = Bullet(by, by)
while True:
# [...]
if my_sprite.rect.colliderect(my_bullet.rect):
printe("hit")
【讨论】:
@Shihabcollidelist
【参考方案2】:
根据我对 pygame 的了解,您只需要使用 colliderect
方法检查两个矩形是否重叠。一种方法是在您的 Bullet
类中使用一个方法来检查冲突:
def is_collided_with(self, sprite):
return self.rect.colliderect(sprite.rect)
那么你可以这样称呼它:
sprite = Sprite(10, 10, 'my_sprite')
bullet = Bullet(20, 10)
if bullet.is_collided_with(sprite):
print('collision!')
bullet.kill()
sprite.kill()
【讨论】:
请注意,如果子弹相对于目标的速度大于目标每刻的宽度,则子弹可以“传送”到目标的另一侧而不会击中。如果是这种情况,您可能需要检查代表子弹从前一帧到当前帧的轨迹的矩形,而不仅仅是子弹本身。【参考方案3】:有一个非常简单的方法可以让您尝试使用内置方法进行操作。
这是一个例子。
import pygame
import sys
class Sprite(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20, 20])
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.center = pos
def main():
pygame.init()
clock = pygame.time.Clock()
fps = 50
bg = [255, 255, 255]
size =[200, 200]
screen = pygame.display.set_mode(size)
player = Sprite([40, 50])
player.move = [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN]
player.vx = 5
player.vy = 5
wall = Sprite([100, 60])
wall_group = pygame.sprite.Group()
wall_group.add(wall)
player_group = pygame.sprite.Group()
player_group.add(player)
# I added loop for a better exit from the game
loop = 1
while loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = 0
key = pygame.key.get_pressed()
for i in range(2):
if key[player.move[i]]:
player.rect.x += player.vx * [-1, 1][i]
for i in range(2):
if key[player.move[2:4][i]]:
player.rect.y += player.vy * [-1, 1][i]
screen.fill(bg)
# first parameter takes a single sprite
# second parameter takes sprite groups
# third parameter is a do kill command if true
# all group objects colliding with the first parameter object will be
# destroyed. The first parameter could be bullets and the second one
# targets although the bullet is not destroyed but can be done with
# simple trick bellow
hit = pygame.sprite.spritecollide(player, wall_group, True)
if hit:
# if collision is detected call a function in your case destroy
# bullet
player.image.fill((255, 255, 255))
player_group.draw(screen)
wall_group.draw(screen)
pygame.display.update()
clock.tick(fps)
pygame.quit()
# sys.exit
if __name__ == '__main__':
main()
【讨论】:
【参考方案4】:为项目符号创建一个组,然后将项目符号添加到该组中。
我要做的是: 在播放器类中:
def collideWithBullet(self):
if pygame.sprite.spritecollideany(self, 'groupName'):
print("CollideWithBullet!!")
return True
在某处的主循环中:
def run(self):
if self.player.collideWithBullet():
print("Game Over")
希望对你有用!!!
【讨论】:
以上是关于如何检测pygame中的碰撞?的主要内容,如果未能解决你的问题,请参考以下文章
pygame.mask原理及使用pygame.mask实现精准碰撞检测
pygame.mask原理及使用pygame.mask实现精准碰撞检测