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'sxvel
.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
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
:
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
Decode HTML Entities in Python String
Extracting Text from Ms Word Files in Python
Linux Command-Line Call Not Returning What It Should from Os.System
Pythonpath Not Working For Sudo on Gnu/Linux (Works For Root)
How to Print Without a Newline or Space
Error: Unable to Find Vcvarsall.Bat
How to Break Out of Multiple Loops
Difference Between Old Style and New Style Classes in Python
Strip HTML from Strings in Python
Pycharm and Sys.Argv Arguments
Tkinter.Photoimage Doesn't Not Support Png Image
Is There a Python Equivalent to Java'S Awt Robot Class
How to Make a Flat List Out of a List of Lists
What Are Metaclasses in Python
What Is Truthy and Falsy? How Is It Different from True and False