How to Detect Collision in Pygame

How to detect collision? pygame

You check for collisions with the killing_entities list for each event in each frame. So if there's currently not event in the event queue, the lines:

if pygame.sprite.spritecollideany(player, killing_entities):
main()

are never executed. I guess you want that code in your main loop only, not in the event handling loop.

There are some more oddities in your code, e.g.

  • You don't handle the QUIT event so you can't close the game's window.

  • You use the same variable running as condition for your main loop and to change the player's xvel.

  • There's also no need to update the display twice (just use pygame.display.flip()).

  • You're blitting a huge (3200x32px) Surface 1024 times per frame to the screen. If you want to fill the screen with the color black, just call screen.fill('black') once per frame.

Python Pygame collision detection between objects

Use a pygame.Rect object and colliderect() to find a collision between a rectangle and an object.

pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. The position of the rectangle can be specified by a keyword argument:

while running:
# [...]

for cb in cubes:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon2, cb) # draw cube

icon_rect = icon2.get_rect(topleft = cb)
if cb[1] > 800 or icon_rect.colliderect(spriterect):
cb[1] = -100 # move to above screen
cb[0] = randint(1, 1000)

# [...]

However, you can simplify your code:

icon_list = [icon2, icon3, icon4, icon5]

cube_lists = [[[
randrange(screen.get_width() - icon.get_width()),
randint(-1500, -350)]
for x in range(2)]
for icon in icon_list]

running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False

keys = pygame.key.get_pressed()
spriterect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * speed
spriterect.y = 600

screen.blit(background_image, (0, 0))
screen.blit(sprite1, spriterect)

for icon, cubes in zip(icon_list, cube_lists):
for cb in cubes:
cb[1] += .25 # cube moves down 2 pixels
screen.blit(icon, cb) # draw cube

icon_rect = icon.get_rect(topleft = cb)
if cb[1] > screen.get_height() or icon_rect.colliderect(spriterect):
cb[:] = randrange(screen.get_width() - icon.get_width()), -800

pygame.display.flip()

pygame.quit()
sys.exit()

Please note, the application loop terminates if running is False. There is no point in setting running = False and to call pygame.quit() and sys.exit() in the event loop. Let the loop run to the end and call pygame.quit() and sys.exit() after the event loop.

how do i add a detect collision in pygame

Collision detection depends on your needs.

  • Bounding boxes are simple and can detect if the x, y edges of each object are not within one another. This is fine for fast moving games and things where you don't necessarily require point precision.
  • Distance vectors are also simple and perfect solution for circle collisions. They calculate the difference in distance between two objects according to a radius prescribed with the hypotenuse of distX^2 + distY^2.
  • Compound bounding objects are harder to calculate, especially for concave areas on objects of arbitrary shape. There are too many notable and novel approaches to collision detection beyond these to remark on here.

They're also increasingly complex depending on things like variability (if they're deformable objects, if they have particle seams, etc and 2d vs 3d object collision can be vastly different worlds as well. There are tons of articles, but I'll post one with implementation here

How to detect collisions between two rectangular objects or images in pygame

Use pygame.Rect objects and colliderect() to detect the collision between the bounding rectangles of 2 objects or 2 images:

rect1 = pygame.Rect(x1, y1, w1, h1)
rect2 = pygame.Rect(x2, y2, w2, h2)
if rect1.colliderect(rect2):
# [...]

If you have to images (pygame.Surface objects), the bounding rectangle of can be get by get_rect(), where the location of the Surface has to be set by an keyword argument, since the returned rectangle always starts at (0, 0):

(see Why is my collision test not working and why is the position of the rectangle of the image always wrong (0, 0)?)

def game_loop():
# [...]

while running:
# [...]

player_rect = player_img.get_rect(topleft = (x, y))
for i in range(len(things_cor)):
thing_rect = things_added[i].get_rect(topleft = things_cor[i])

if player_rect.colliderect(thing_rect):
print("hit")

player(x, y)
x += x_change

for i in range(len(things_cor)):
thing_x, thing_y = things_cor[i]
things(thing_x, thing_y, things_added[i])

Use pygame.time.get_ticks() to delay the start of the game for a certain time. pygame.time.get_ticks() return the number of milliseconds since pygame.init() was called. For instance:

def game_loop():
# [...]

while running:
passed_time = pygame.time.get_ticks() # passed time in milliseconds
start_time = 100 * 1000 # start time in milliseconds (100 seconds)

# [...]

# move player
if passed_time >= start_time:
x += x_change
if x < 0:
x = 0
elif x > display_width - player_width:
x = display_width - player_width

# move things
if passed_time >= start_time:
for i in range(len(things_cor)):
things_cor[i][1] += y_change
if things_cor[i][1] > display_height:
things_cor[i][1] = random.randint(-2000, -1000)
things_cor[i][0] = random.randint(0, display_width)
things_added[i] = random.choice(thing_imgs)

things_added.append(random.choice(thing_imgs))

if len(things_added) < 6:
things_cor.append(
[random.randint(0, display_width), -10])

# draw scene and update dispaly
game_display.fill(white)
player(x, y)
for i in range(len(things_cor)):
thing_x, thing_y = things_cor[i]
things(thing_x, thing_y, things_added[i])
pygame.display.update()
clock.tick(60)

How to do a collision detection between line and rectangle in pygame?

See Make a line as a sprite with its own collision in Pygame

Check collision between a image and a line

You need to create a image (pygame.Surface) in with a per pixel alpha format (pygame.SRCALPHA) or a black color key (pygame.Surface.get_colorkey). Draw the line on the image and blit the image on the screen:

class Line(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((200, 200))
self.image.set_colorkey((0, 0, 0))
pygame.draw.line(self.image, (255, 0, 0), (0, 0), (200, 200))
self.rect = self.image.get_rect()
self.rect.x = 50
self.rect.y = 0

def update(self):
screen.blit(self.image, self.rect)

Do the same for the rectangle:

class Rectt(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.set_colorkey((0, 0, 0))
pygame.draw.rect(self.image, (0, 255, 0), (0, 0, 50, 50))
self.rect = self.image.get_rect()
self.rect.x = 25
self.rect.y = 100

def update(self):
screen.blit(self.image, self.rect)

You do not need the update methods at all if you are using a pygame.sprite.Group and pygame.sprite.Group.draw:

Sample Image

import pygame

class Line(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((200, 200))
self.image.set_colorkey((0, 0, 0))
pygame.draw.line(self.image, (255, 255, 0), (0, 0), (200, 200), 5)
self.rect = self.image.get_rect(topleft = (50, 50))

class Rectt(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.set_colorkey((0, 0, 0))
pygame.draw.rect(self.image, (0, 0, 255), (0, 0, 50, 50))
self.rect = self.image.get_rect(topleft = (25, 100))

pygame.init()
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

running = True
l = Line()
m = Rectt()
group = pygame.sprite.Group([l, m])

while running:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False

keys = pygame.key.get_pressed()
m.rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3
m.rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3
m.rect.clamp_ip(screen.get_rect())
hit = pygame.sprite.collide_mask(m, l)

screen.fill((0, 0, 0))
group.draw(screen)
if hit:
pygame.draw.rect(screen, (255, 0, 0), m.rect, 5)
pygame.display.flip()

pygame.quit()

how to prevent two masks from overlapping in pygame?

I think the issue is that your program is allowing the overlap in the first place. Once they're colliding you can't do anything.

Before moving the object, check that the destination location is not already occupied by doing a "future collision" check. If there's going to be a collision, then either don't allow the movement at all, or handle it in some nicer way.

If you know the direction of movement - say the player pushed , and is moving left. The code can easily move the player as far left as possible, to the point just before colliding.

This way you never have to deal with objects atop each other.

It's not really clear to me what approach the program is taking. The API pygame.mask.overlap_area() returns the number of bits overlapping. The code is calculating the collision normal, not trying to prevent or undo the overlap. Maybe it can move each object by the inverse of this direction, or suchlike.



Related Topics



Leave a reply



Submit