Search code examples
pythonpygamecollision-detectioncollisionrect

argument must be rect style object - Pygame


DISCLAIMER: I know other people on this site have had this error but it's been in different contexts and the fixes don't seem to work here.

Hey stackoverflow, Basically I've been making a sideways space invaders game, where your ship follows your mouse's Y position but stays on the left, and clicking shoots a bullet. Enemies spawn randomly from the right and fly to the left, and objects are removed once they each the side of the screen. This all works fine, however when I tried to implement collision detection to see when the shot hits the enemy, I get the error:

TypeError: Argument must be rect style object.

It points to the line "return hitbox.colliderect(target.rect)". To try and work out what was causing this I made some 'print' lines below it to see what 'target.rect' was (and whether it was a proper rect object), and it seemed to be exactly the same style as the shot's own rect, so I'm completely lost as to why colliderect doesn't accept it.

I've annotated pretty much everything, sorry if it's an insult to your intelligence but it should make things easier.

Class that causes problems:

class gameObject:
def __init__(self, image, height, speed):
    self.speed = speed
    self.image, self.rect = load_image("shot.dsf", -1) #Loads the image and creates rectange 
    self.pos = image.get_rect().move(0, height)
def move(self):
    self.pos = self.pos.move(self.speed, 0) #Moves along
    self.rect = [self.pos, 32, 32] #Updates the position of the rectange. Without it the rectangle just stays in the top left corner but the image moves
    if self. pos.right > 1400: #Checks if shot goes offscreen
        objects.remove(self) #Removes it
def hitreg(self,target):
    hitbox = self.rect #Creates hitbox of it's own rectangle
    return hitbox.colliderect(target.rect) #SHOULD return true if hitbox collides with the rect of target, however it returns the error
    print("OWN RECT: ",self.rect) #Debug purposes, returns ('OWN RECT: [<rect(1350, 245, 32, 32)>, 32, 32])
    print("TARGET RECT: ",target.rect) #Debug purposes, returns ('TARGET RECT: [<rect(360, 479, 128, 64)>, 128, 64])

Main Loop

r.drawFrame() #Renders Frame
while running:
sleep(0.0166) #Make sure it only renders at most 60fps
rand = rnd(10,20) #Generates random number from 10 to 20
pos = pygame.mouse.get_pos() #Gets mouse position
for event in pygame.event.get():
    if event.type == MOUSEBUTTONUP: #Prevents hang
        None 
    if event.type == MOUSEBUTTONDOWN: #Creates shot if mousebuttondown, at mouse height with random speed
        o = gameObject(shot, pos[1], rand)
        objects.append(o)
if rnd(1,120) == 60: #Randomely Spawns Enemy Ships
    s = enemyShip(enemya, rnd(10,890), rand) #enemya is filename
    ships.append(s)
for o in objects: #For each shot
    for s in ships: #For each enemy ship
        if o.hitreg(s): #Calls hitreg function, s being the target
            s.takedamage()
            score = score + 10 #Increases score
r.drawFrame() #Renders Frame

Solution

  • If the output shown in your source code can be trusted, your debug statements show that neither self.rect nor target.rect are "rect style" objects. They appear to be lists consisting of a rect followed by 2 integers such as this:

    OWN RECT: [<rect(1350, 245, 32, 32)>, 32, 32])
    TARGET RECT: [<rect(360, 479, 128, 64)>, 128, 64]
    

    The move() method is responsible for changing self.rect from a "normal" rect into a list as above, but this modified self.rect will not behave like a rect. Perhaps in hitreg() you should be calling hitbox = self.rect[0].

    Anyway, in this case, I suspect that the output of your debug statements as shown is incorrect. Probably the value for self.rect is a rect (because lists do not have method colliderect(), but colliderect() does seem to be called), and it is target.rect that is a list. target.rect is of type enemyShip, but you don't show the code for that, so it is not possible to tell what is actually going on there (although one could guess that move() has been called on it and it's self.rect corrupted).

    I suggest that you capture some real debug output and add that to your question. It would also be helpful if you could post accurate code, because there is at least 1 syntax error, e.g. in the line:

    if self. pos.right > 1400:
    

    And what does the code for enemyShip look like?