Collision Between Masks in Pygame

Collision between masks in pygame

The mask for the Terrain is never set. Crate a proper Terrain mask:

class Terrain(object):
def __init__(self, x, y):
self.x = x
self.y = y

maskSurf = pg.Surface((display_width, display_height)).convert_alpha()
maskSurf.fill(0)
pg.draw.rect(maskSurf, (160, 160, 160), (self.x, self.y, display_width, 500), 0)
self.mask = pg.mask.from_surface(maskSurf)
print(self.mask.count())

# [...]

When using pygame.mask.Mask.overlap(), then you've to check the overlapping of the Spaceship and the Terrain, rather than the Terrain and the Spaceship.

Since the Terrain mask is a mask of the entire screen, the offset for the overlap() test is the position of the Spaceship:

def check_for_collisions():
offset = (int(spaceship.x), int(spaceship.y))
collide = terrain.mask.overlap(spaceship.mask, offset)
print(offset, collide)
return collide

Minimal example: Sample Image repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect

Sample Image

See also: Mask

Pygame mask collision

Your application works fine. But note, pygame.sprite.collide_mask() use the .rect and .mask attribute of the sprite object for the collision detection.

You have to update self.rect after rotating the image:

class Box(pygame.sprite.Sprite):
# [...]

def rotate(self):
self.angle += 3
new_img = pygame.transform.rotate(self.image, self.angle)
new_rect = new_img.get_rect(center=self.rect.center)

# update .rect attribute
self.rect = new_rect # <------

return new_img, new_rect

See also Sprite mask


Minimal example: Sample Image repl.it/@Rabbid76/PyGame-SpriteMask

Sample Image

import pygame

class SpriteObject(pygame.sprite.Sprite):
def __init__(self, x, y, w, h, color):
pygame.sprite.Sprite.__init__(self)
self.angle = 0
self.original_image = pygame.Surface([w, h], pygame.SRCALPHA)
self.original_image.fill(color)
self.image = self.original_image
self.rect = self.image.get_rect(center = (x, y))
self.mask = pygame.mask.from_surface(self.image )
def update(self):
self.rotate()
def rotate(self):
self.angle += 0.3
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(center = self.rect.center)
self.mask = pygame.mask.from_surface(self.image )

pygame.init()
clock = pygame.time.Clock()
window = pygame.display.set_mode((400, 400))
size = window.get_size()

moving_object = SpriteObject(0, 0, 50, 50, (128, 0, 255))
static_objects = [
SpriteObject(size[0] // 2, size[1] // 3, 100, 50, (128, 128, 128)),
SpriteObject(size[0] // 4, size[1] * 2 // 3, 100, 50, (128, 128, 128)),
SpriteObject(size[0] * 3 // 4, size[1] * 2 // 3, 100, 50, (128, 128, 128))
]
all_sprites = pygame.sprite.Group([moving_object] + static_objects)
static_sprites = pygame.sprite.Group(static_objects)

run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

moving_object.rect.center = pygame.mouse.get_pos()
all_sprites.update()
collide = pygame.sprite.spritecollide(moving_object, static_sprites, False, pygame.sprite.collide_mask)

window.fill((255, 0, 0) if collide else (255, 255, 255))
all_sprites.draw(window)
pygame.display.update()

pygame.quit()
exit()

Pygame collision with masks

The offset parameter of the method overlap() is the relative position of the othermask in relation to the pygame.mask.Mask object.

So the offset is calculated by subtracting the coordinates of slant from the coordinates of ball:

offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)

offset = (ball.rect.x - slant.rect.x), (ball.rect.y - slant.rect.y)
if slant.mask.overlap(ball.mask, offset):
print("hit")


When you create the mask images, then I recommend to ensure that the image has per pixel alpha format by calling .convert_alpha():

class Ball:

def __init__(self, x, y):
self.x = x
self.y = y

self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.convert_alpha()) # <---
class Slant:

def __init__(self, x, y):
self.x = x
self.y = y

self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.image.convert_alpha()) # <---

Minimal example: Sample Image repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect

Sample Image

See also: Mask

pygame - mask collision not working, return always same collision point

See PyGame collision with masks. Your code can't work, because of offset = 0, 0. The offset must be the distance between the top left corners of the images:

offset = (character_x - room_x), (charcater_y - room_y)
collision = characterhitbox_mask.overlap(collisionsroom1_mask, offset)

In the example above, (room_x, room_y) is the position of the room and (character_x, character_y) is the position of the character.

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.

Pygame masks Python

A mask is created from an image. The masks are just a grid with Boolean values. One field in the mask corresponds to one pixel in the surface. If it is True, the pixel in the corresponding image belongs to the colored sprite. If False, the pixel in the corresponding image belongs to the background. A pygame.mask.Mask object has no position.

The pygame.mask.Mask.overlap method checks whether the objects overlap when they are placed on the screen. Since the objects will not be placed exactly in the same place, you need to specify the offset. See PyGame collision with masks is not working and Collision between masks in PyGame.

+---------------+
| mask 1 . |
| oy |
| . |
|... ox ...+-------------+
| | | |
| | | |
+----------|----+ |
| |
| mask 2 |
+-------------+

ox is offset_x

oy is offset_y

Minimal example: Sample Image repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect

Sample Image

See also: Mask


When you use pygame.sprite.Sprite objects, you don't need to compute the offset. You can us pygame.sprite.collide_mask, which computes the offsets from the .rect attrbutes. See How can I made a collision mask? and Pygame mask collision

Minimal example: Sample Image repl.it/@Rabbid76/PyGame-SpriteMask

Sample Image

See also Sprite mask

Pygame mask for pixel perfect collision between player sprite and platform sprites

you could check to see if the character is touching the platform ( using the mask.overlap method) and then set the velocity to 0 and gradually increase the y value until they are no longer colliding then continue with the rest of the game loop. (this is how I checked for collision but I used images instead of sprites) hope this helps.



Related Topics



Leave a reply



Submit