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: repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect
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: repl.it/@Rabbid76/PyGame-SpriteMask
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: repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect
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: repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect
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: repl.it/@Rabbid76/PyGame-SpriteMask
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
How to Set Class Attributes from Variable Arguments (Kwargs) in Python
How to Create a Custom Activation Function with Keras
Consistently Create Same Random Numpy Array
Read and Overwrite a File in Python
How to Sort a List of Tuples According to Another List
How to Generate a Random Number with a Specific Amount of Digits
Prime Number Check Acts Strange