How can I made a collision mask? - python

I try to make a collision mask to detect if two sprites collide themselves, but It's not working at all, I got an instant crash, Can you help me ?
My code is :
Player.rect = Player.image.get_rect()
oc1.rect = oc1.image.get_rect()
mask_1 = pg.mask.from_surface(Player)
mask_2 = pg.mask.from_surface(oc1)
Cm = pg.sprite.collide_mask(mask_1, mask_2)
if Cm != None :
print('life - 1')

See the documentation of pygame.sprite.collide_mask():
Collision detection between two sprites, using masks.
collide_mask(SpriteLeft, SpriteRight) -> point
Tests for collision between two sprites, by testing if their bitmasks overlap. If the sprites have a "mask" attribute, that is used as the mask, otherwise a mask is created from the sprite image. Intended to be passed as a collided callback function to the *collide functions. Sprites must have a "rect" and an optional "mask" attribute.
The parameters to .collide_mask() have to be 2 pygame.sprite.Sprite objects, rather than 2 mask pygame.mask.Mask objects:
In the following is assumed, that Player and oc1 are pygame.sprite.Sprite objects:
Player.rect = Player.image.get_rect()
oc1.rect = oc1.image.get_rect()
Cm = pg.sprite.collide_mask(Player, oc1)
if Cm != None :
print('life - 1')
Minimal example: repl.it/#Rabbid76/PyGame-SpriteMask
See also Sprite mask

Related

Easy Way To Detect Collisions With Window Edge In Pygame?

I am making a non-scrolling platformer in pygame, and am wondering if there is an easy way to detect collisions with the edge of the window, without creating four rects offscreen. Does anyone know if there is? Thanks.
If you're testing collisions for Rects, you can use
if (Rect.left < 0 or Rect.right > (window width) or
Rect.top < 0 or Rect.bottom > (window height)):
collision = True # do whatever collision code you need here
If you need a way to get the screen size, you can use
width, height = pygame.display.get_surface().get_size()
and then use the width and height variables.
You can use pygame.Rect.contains to test if a rectangle is entirely inside another rectangle:
window_rect = screen.get_rect()
if not window_rect.contains(object_rec):
# [...]
Use pygame.Rect.colliderect to test if a rectangle is entirely outside another rectangle:
window_rect = screen.get_rect()
if not window_rect.colliderect(object_rec):
# [...]

How to make mask collision detection less laggy in python pygame

Im trying to make a collision detection system for a game im making. This section of code works fine when I am detecting collisions when the objects are smaller, but now once I make a mask of a moon which can be up to 1000 pixels in diameter it starts to lag my computer. Ive tried to make a 2nd image of the outline of the moon to use to detect the collisions but upon further testing it would still detect collisions while in the middle of the outline (in the transparent parts of the image) and did not help the lag. I also tried to make the outline image less pixels but keeping the same size. The images move down the screen as a ship moves across the screen to dodge them. I need pixel perfect collisions
If someone could tell me how to reduce lag, or some other way of detecting if my ship is within the circle that would be a big help :)
for i in range(numMoon):
moonRect = pygame.Rect(moonX[i], moonY[i], int(100*moonScale[i]), int(100*moonScale[i]))
if moonRect.colliderect(shipRect):
moonMask = pygame.mask.from_surface(moon)
offset_x = shipRect.x - moonRect.x
offset_y = shipRect.y - moonRect.y
crash = moonMask.overlap(shipMask, (offset_x, offset_y))
if crash:
print('moon')
Creating a Mask from a Surface is an expensive operation. Do not generate the pygame.mask in the loop. Create the mask during initialization directly after loading the image:
moon = pygame.image.load(...)
moonMask = pygame.mask.from_surface(moon)
Use the pre-generated mask
for i in range(numMoon):
moonRect = pygame.Rect(moonX[i], moonY[i], int(100*moonScale[i]), int(100*moonScale[i]))
if moonRect.colliderect(shipRect):
offset_x = shipRect.x - moonRect.x
offset_y = shipRect.y - moonRect.y
crash = moonMask.overlap(shipMask, (offset_x, offset_y))
if crash:
print('moon')

How to use pixel perfect collision with two images pygame

Note: I do not want to use pygame sprites.
I am making a simple game and I need a way to detect if two images overlap using pixel-perfect collision in pygame. All the answers I have found so far require that I use pygame sprites, which I prefer not to use because I have less control over the objects.
(These images have transparent backgrounds)
First of all, don't be afraid of Sprites.
A Sprite is just a simple class with an image (a Surface that is stored in the image attribute) and the size and position of the image (a Rect stored in the rect attribute).
So when you use a class like this:
class Player:
def __init__(self, image, pos):
self.image = image
self.pos = pos
def draw(self, screen):
screen.blit(self.image, self.pos)
you could simple use the Sprite class instead, since not much would change:
class Player(pygame.sprite.Sprite):
def __init__(self, image, pos):
super().__init__()
self.image = image
self.rect = image.get_rect(center=pos)
Instead, it becames simpler, because we can let pygame handle blitting the image to the screen.
So, to use pixel perfect collision, you can use pygame's Mask class. Use pygame.mask.from_surface to create a Mask from your Surface, and use pygame.mask.Mask.overlap to check if two masks overlap.
It's easier to use when you use the Sprite class, since you could just use functions like spritecollide together with collide_mask.
But if you don't want to use the Sprite class, just take a look how collide_mask is implemented to see how you can use masks:
def collide_mask(left, right):
xoffset = right.rect[0] - left.rect[0]
yoffset = right.rect[1] - left.rect[1]
try:
leftmask = left.mask
except AttributeError:
leftmask = from_surface(left.image)
try:
rightmask = right.mask
except AttributeError:
rightmask = from_surface(right.image)
return leftmask.overlap(rightmask, (xoffset, yoffset))

Get the pixels which are colliding with the Surface

Right now, I have a collision detection in pygame which checks if two rctangles overlap or not...what I am trying to do is check the transparency of a the surface and if the alpha value is less then 10, stop player from walking into it..
Currently, Im doing this:
for i in range(0,self.rect.w):
for j in range(0,self.rect.h):
if player.rect.collidepoint((i,j)) and self.image.get_at((i,j))[3]<10:
#STOP PLAYER
But it is a real pain on the Processor. Is there another way to get the collision pixel coordinates in pygame??
Use pygame.mask.Mask objects and overlap() or overlap_mask().
overlap() :
Returns the first point of intersection encountered between this mask and othermask.
[...]
Returns point of intersection or None if no intersection.
overlap_mask():
Returns a Mask, the same size as this mask, containing the overlapping set bits between this mask and othermask.
A mask can be created form a pyame.Surface with pygame.mask.from_surface()-
e.g.:
player_mask = pygame.mask.from_surface(player.image)
self.mask = pygame.mask.from_surface(self.image)
offset = (player.rect.x - self.rect.x), (player.rect.y - self.rect.y)
first_intersection_point = self.mask.overlap(player_mask , offset)
if first_intersection_point:
print("hit")

PyGame Platformer with Interactive Platforms "Drawn" In

I'm looking for the easiest way to implement this. I'm trying to implement platforms (with full collision detection) that you can draw in via mouse. Right now I have a line drawing function that actually draws small circles, but they're so close together that they more or less look like a line. Would the best solution be to create little pygame.Rect objects at each circle? That's going to be a lot of rect objects. It's not an image so pixel perfect doesn't seem like an option?
def drawGradientLine(screen, index, start, end, width, color_mode):
#color values change based on index
cvar1 = max(0, min(255, 9 * index-256))
cvar2 = max(0, min(255, 9 * index))
#green(0,255,0), blue(0,0,255), red(255,0,0), yellow(255,255,0)
if color_mode == 'green':
color = (cvar1, cvar2, cvar1)
elif color_mode == 'blue':
color = (cvar1, cvar1, cvar2)
elif color_mode == 'red':
color = (cvar2, cvar1, cvar1)
elif color_mode == 'yellow':
color = (cvar2, cvar2, cvar1)
dx = end[0] - start[0]
dy = end[1] - start[1]
dist = max(abs(dx), abs(dy))
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx)
y = int(start[1]+float(i)/dist*dy)
pygame.draw.circle(screen, color, (x, y), width)
That's my drawing function. And here's my loop that I have put in my main game event loop.
i = 0
while (i < len(pointList)-1):
drawGradientLine(screen, i, pointList[i], pointList[i + 1], r, mode)
i += 1
Thanks for any help, collision detection is giving me a huge headache right now (still can't get it right for my tiles either..).
Any reason you want to stick with circles?
Rectangles will make the line/rectangle a lot more smooth and will make collision detecting a lot easier unless you want to look into pixel perfect collision.
You also don't seem to save your drawn objects anywhere (like in a list or spritegroup), so how are you going to check for collision?
Here's a leveleditor I did for game awhile back, it's not perfect, but it works:
https://gist.github.com/marcusmoller/bae9ea310999db8d8d95
How it works:
The whole game level is divided up into 10x10px grid for easier drawing
The leveleditor check if the mouse is being clicked and then saves that mouse position
The player now moves the mouse to another position and releases the mouse button, the leveleditor now saves that new position.
You now have two different coordinates and can easily make a rectangle out of them.
Instead of creating a whole bunch of rect objects to test collision against, I'm going to recommend creating something called a mask of the drawn-in collideable object, and test for collision against that. Basically, a mask is a map of which pixels are being used and which are not in an image. You can almost think of it as a shadow or silhouette of a surface.
When you call pygame.draw.circle, you are already passing in a surface. Right now you are drawing directly to the screen, which might not be as useful for what I'm suggesting. I would recommend creating a rect which covers the entire area of the line being drawn, and then creating a surface of that size, and then draw the line to this surface. My code will assume you already know the bounds of the line's points.
line_rect = pygame.Rect(leftmost, topmost, rightmost - leftmost, bottommost - topmost)
line_surf = pygame.Surface((line_rect.width, line_rect.height))
In your drawGradientLine function, you'll have to translate the point coordinates to the object space of the line_surf.
while (i < len(pointList)-1):
drawGradientLine(line_surf, (line_rect.x, line_rect.y), i, pointList[i], pointList[i+1], r, mode)
i += 1
def drawGradientLine(surf, offset, index, start, end, width, color_mode):
# the code leading up to where you draw the circle...
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx) - offset[0]
y = int(start[1]+float(i)/dist*dy) - offset[1]
pygame.draw.circle(surf, color, (x, y), width)
Now you'll have a surface with the drawn object blitted to it. Note that you might have to add some padding to the surface when you create it if the width of the lines you are drawing is greater than 1.
Now that you have the surface, you will want to create the mask of it.
surf_mask = pygame.mask.from_surface(line_surf)
Hopefully this isn't getting too complicated for you! Now you can either check each "active" point in the mask for collision within a rect from your player (or whatever other objects you want to collide withe drawn-in platforms), or you can create a mask from the surface of such a player object and use the pygame.Mask.overlap_area function to check for pixel-perfect collision.
# player_surf is a surface object I am imagining exists
# player_rect is a rect object I am imagining exists
overlap_count = surf_mask.overlap_area(player_surf, (line_rect.x - player_rect.x, line_rect.y - player_rect.y))
overlap_count should be a count of the number of pixels that are overlapping between the masks. If this is greater than zero, then you know there has been a collision.
Here is the documentation for pygame.Mask.overlap_area: http://www.pygame.org/docs/ref/mask.html#pygame.mask.Mask.overlap_area

Categories