0

I am creating tetris using pygame. i want to use collision detection so that when the shape in play comes into contact with any other previously played shapes, i can stop the shape, as per the logic of tetris. i came across pixel perfect collision using masks. i have followed some tutorials online, however the pixel detection returns true every time a new shape comes into play, not when any shapes collide. sorry in advance for the long code, its the bare minimum for the code to actually and still containing the game element of it. i think there is something wrong with my approach which is causing this error. I basically have a function that everytime the shape in play comes into contact with the 'floor' that shape is held in that position and a new shape is created. i think ive overcomplicated it, in turn creating this error. thanks in advance

import pygame
import sys
import shapelogic
pygame.init()
screensize = width, height = 800, 595
screen = pygame.display.set_mode(screensize)
background_image =pygame.image.load("/Users/marceason/PycharmProjects/Tetris/Wooden_background.jpg").convert_alpha()
myshape = 0
stop_movement = 0
blit_count = 0
stored_shapes = pygame.sprite.Group()
stored_shapes_with_coords = []
extra_blit_required = False
index = 0
count = 0
listofshapes = []
class shapemanager():
 def __init__(self):
 self.listofshapes = []
 def create_another_instance(self):
 global count
 count += 1
 string = "Shape_{0},".format(count)
 another_shape = Shape(string)
 self.listofshapes.append(another_shape)
 global index
 object = self.listofshapes[index]
 index += 1
 return object
def load_shape(self):
 shape = self.create_another_instance()
 shape.load_shapes()
class Shape(pygame.sprite.Sprite):
def __init__(self, name):
 pygame.sprite.Sprite.__init__(self)
 self.name = name
 self.x = 50
 self.y = 100
 self.move_event = pygame.USEREVENT + 1
 self.reached_bottom_event = pygame.USEREVENT + 2
 self.one_sec_timer = 1000
 self.half_sec_timer = 500
 self.reachbottomflag = False
 self.movement_possible = True
 self.image = pygame.image.load(
"/Users/marceason/PycharmProjects/Tetris/Tetris_Shapes/Green_Shape_1_Position_1.png")
 self.mask = pygame.mask.from_surface(self.image)
 self.rect = self.image.get_rect()
def move_shape(self):
 if self.movement_possible:
 key_input = pygame.key.get_pressed()
 if key_input[pygame.K_LEFT]:
 self.x -= 16
 if key_input[pygame.K_RIGHT]:
 self.x += 16
 if not self.reachbottomflag:
 if key_input[pygame.K_DOWN]:
 self.y += 16
def reachbottom(self):
 if self.y >= 560:
 self.reachbottomflag = True
def no_movement_possible(self):
 self.movement_possible = False
def assign_shape():
global myshape
global stop_movement
myshape = sl.create_another_instance()
pygame.time.set_timer(myshape.move_event, myshape.one_sec_timer)
stop_movement = pygame.time.set_timer(myshape.reached_bottom_event, myshape.half_sec_timer)
def blit_used_shapes():
global screen
global blit_count
blit_count = len(stored_shapes_with_coords)
local_count = 0
while local_count < blit_count:
 screen.blit(stored_shapes_with_coords[local_count][0], (stored_shapes_with_coords[local_count][1], stored_shapes_with_coords[local_count][2]))
 local_count += 1
sl = shapemanager()
##### HERE IS THE PIXEL DETECTION #####
result = pygame.sprite.spritecollide(myshape, stored_shapes, False, pygame.sprite.collide_mask)
## Main loop ##
assign_shape()
while True:
for event in pygame.event.get():
 if event.type == pygame.QUIT: sys.exit()
 screen.blit(background_image, (0, 0))
 screen.blit(myshape.image, (myshape.x, myshape.y))
 myshape.move_shape()
 key_input = pygame.key.get_pressed()
 if key_input[pygame.K_SPACE]:
 myshape.rotate_shape()
 myshape.reachbottom()
 if myshape.reachbottomflag:
 if event.type == myshape.reached_bottom_event:
 myshape.no_movement_possible()
 stored_shape_tuple = [myshape.image, myshape.x, myshape.y]
 stored_shapes_with_coords.append(stored_shape_tuple)
 stored_shapes.add(myshape)
 extra_blit_required = True
 assign_shape()
 ####### PIXEL DETECTION IS HERE IN FOR LOOP ####
 if result:
 print("this should only execute when two shapes touch!!")
 if extra_blit_required:
 blit_used_shapes()
 pygame.display.update()
asked Aug 4, 2020 at 21:31
7
  • why to use perfect collision for squares ? It is only waste of time. Commented Aug 4, 2020 at 22:47
  • you should use print() to check values in variables - it is called print debuging. OR learn how to use real debuger. Maybe it checks collision with different elements then you expect and this gives always True. Sometimes people use .x, .y to move object but they forgot that pygame uses .rect to check collision and they forgot to change .rect.x, .rect.y. You should use only .rect.x and .rect.y to move and draw it blit(shape.image, shape.rect) Commented Aug 4, 2020 at 22:51
  • BTW: instead of checking self.x >= 560 you could check self.rect.bottom >= 595 (widow's height) Commented Aug 4, 2020 at 22:57
  • BTW: if you use classes then instead of global index, count you could use self.index and self.count Commented Aug 4, 2020 at 22:59
  • your code has wrong indentations so it is useless - we can't run it. Always put code with correct indentations and use button {} or shortcut Ctrl+K to format it in question. Commented Aug 4, 2020 at 23:00

1 Answer 1

3

The issue is that you are not updating the sprites rect attribute. The sprites rects all have position (0, 0) (since you do not set it in the call to self.image.get_rect()) and as a result the masks will all overlap and collide.

If you read the docs for pygame.sprite.collide_mask you will note that it says that your sprites need to have mask and rect attributes. You have a rect in your sprite and you set it in the __init__(), but you do not keep it updated when you move the sprite. You just change the x and y attributes without adjusting the rect position. The reason that the collide_mask wants a rect is that it uses that to determine the offset parameter for the pygame.mask.Mask.overlap() call that it uses. The important thing to realize is that masks themselves do not have a position, they need the rects to determine the relative positions of the masks.

This is similar to images/surfaces not having a position and needing a rect to track that for them.

On a separate issue, the way you are blit'ing the sprites to the screen makes no sense. You are not using the abilities of the sprite groups to draw and worse you are keeping the image, x and y of the sprite in a separate list and not containing it in the sprite itself. You should go look at some examples of pygame sprite based code. There are lots of examples out there.

answered Aug 5, 2020 at 4:07
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for this detailed answer, you have helped me understand masks a little bit more.i have amended the code as to update rects and have changed the way images are blitted to the screen (using a for loop through the sprite group ). the funny thing is now the collision NEVER activates.....
@Pyson The rect is also used to simplify the collision. Doing a mask-based collision is a relatively expensive operation, so the rects are first checked to see if they collide. If the rect's do not collide, then there is not even a need to check the masks.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.