2
votes

I am working on a game in pygame. I have a character sprite and a ground which are both images. I want to detect collision between those objects using masks. Here is my code:

import pygame
import os
import sys
import time
import gradients


pygame.init()

#window size
screen_width = 1080
screen_height = 720
monitor_size = [pygame.display.Info().current_w, pygame.display.Info().current_h]

win = pygame.display.set_mode((screen_width, screen_height), pygame.RESIZABLE)
fullscreen = False
in_options = False


pygame.display.set_caption("Pokemon Firered")




#time for FPS management
clock = pygame.time.Clock()



class character(object):
    def __init__(self,x,y):
        self.left = [pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_1.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_2.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_3.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_4.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_5.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_6.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_7.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_8.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_9.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_10.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_11.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_12.png")).convert_alpha()]
        self.right = [pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_1.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_2.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_3.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_4.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_5.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_6.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_7.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_8.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_9.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_10.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_11.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_12.png")).convert_alpha()]

        self.x = x
        self.y = y
        self.is_right = False
        self.is_left = True
        self.is_jump = False
        self.velocity = 5
        self.walk_count = 0
        self.jump_count= 10
        self.latest_direction = "left"
        self.border = 1000
        if self.latest_direction == "left":
            self.mask = pygame.mask.from_surface(self.left[self.walk_count])
        if self.latest_direction == "right":
            self.mask = pygame.mask.from_surface(self.right[self.walk_count])

    def jump(self, win):
        neg = 1
        if self.jump_count >= -10:
            if self.jump_count < 0:
                neg = -1  
            self.y -= (self.jump_count**2)//2.5 * neg
            self.jump_count -= 1
        else:
            self.is_jump = False
            self.jump_count = 10

    def movement(self, win):
        keys = pygame.key.get_pressed() #keyboard input
        if keys[pygame.K_a] and self.x > -20:
            self.is_left = True
            self.is_right = False
            self.latest_direction = "left"
            self.x -= self.velocity
        elif keys[pygame.K_d] and self.x < self.border:
            self.is_left = False
            self.is_right = True
            self.latest_direction = "right"
            self.x += self.velocity
        else:
            self.is_left = False
            self.is_right = False
        if keys[pygame.K_SPACE] and self.x > 5:
            self.is_jump = True

        if self.is_jump:    
            self.jump(win)

    def draw(self, win):
        if self.walk_count + 1 >= 36:
            self.walk_count = 0
        if self.is_left:
            win.blit(self.left[self.walk_count//3], (self.x, self.y))
            self.walk_count += 1
        elif self.is_right:
            win.blit(self.right[self.walk_count//3], (self.x, self.y))
            self.walk_count += 1
        elif self.is_jump:
            if self.latest_direction == "left":
                win.blit(self.left[self.walk_count//3], (self.x, self.y))
            if self.latest_direction == "right":
                win.blit(self.right[self.walk_count//3], (self.x, self.y))
        else:
            if self.latest_direction == "left":
                win.blit(self.left[3], (self.x, self.y))
            if self.latest_direction == "right":
                win.blit(self.right[3], (self.x, self.y))



avatar = character(500, 350)

class controls_panel(object):
    #controls panel

class ground(object):
    def __init__(self):
        self.ground = pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Background", "ground.png")).convert_alpha()
        self.mask = pygame.mask.from_surface(self.ground)

    def draw(self, win):
        #ground
        ground_height = 502
        ground_width = 590
        repeat = screen_width//590+1
        for i in range(repeat):
            win.blit(self.ground, (0+590*i,ground_height))
            ground_width = 590*(i+1)


ground = ground()


def event_handling(win):
    global fullscreen
    global run
    global screen_width
    global in_options
    for event in pygame.event.get():
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_ESCAPE:
                run = False
        if event.type == pygame.VIDEORESIZE:
            if not fullscreen:
                screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
                avatar.border = event.w-75
                screen_width = event.w
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_f:
                fullscreen = not fullscreen
                if fullscreen:
                    screen = pygame.display.set_mode(monitor_size, pygame.FULLSCREEN)
                else:
                    screen = pygame.display.set_mode((screen.get_width(), screen.get_height()), pygame.RESIZABLE)
            if event.key == pygame.K_e:
                in_options = not in_options

#texts
font = pygame.font.SysFont(None, 25)
def print_text(msg, colour, cords):
    text = font.render(msg, True, colour)
    win.blit(text, cords)

def options(win):
    #options menu


def redraw_game_window():
    event_handling(win)
    #display background
    #win.fill(0)


    #win.fill(0)

    background_gradient = gradients.vertical([screen_width, screen_height], (209, 77, 135, 255), (249, 175, 88, 255))
    win.blit(background_gradient, (0,0))

    #ground
    ground.draw(win)

    if pygame.sprite.spritecollide(avatar, ground, False, pygame.sprite.collide_mask):
        print(True)

    #options
    if in_options:
     options(win)

    #ingame controls 
    controls_panel.input(win)

    #character
    avatar.movement(win)
    avatar.draw(win)

    pygame.display.update()

run = True
while run:
    clock.tick(36) #FPS rate
    redraw_game_window()




#Mainloop End 
pygame.quit()

It is still very messy because I've just started and I left a few unnecessary bits out to keep it a bit more legible. When I try to detect the collision in the redraw_game_window() function, I get the following error message:

Traceback (most recent call last): File "c:/Users/Pc/Desktop/Python/Games/Game Engine/First Game.py", line 234, in redraw_game_window() File "c:/Users/Pc/Desktop/Python/Games/Game Engine/First Game.py", line 215, in redraw_game_window if pygame.sprite.spritecollide(avatar, ground, False, pygame.sprite.collide_mask): File "C:\Users\Pc\AppData\Roaming\Python\Python37\site-packages\pygame\sprite.py", line 1532, in spritecollide return [s for s in group if collided(sprite, s)] TypeError: 'ground' object is not iterable

What I've tried:

  • using rectangles as masks (produces the same error)

Does anyone know what I am doing wrong?

------

Updated classes:

class character(pygame.sprite.Sprite):
    def __init__(self,x,y):
        pygame.sprite.Sprite.__init__(self)
        self.left = [pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_1.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_2.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_3.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_4.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_5.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_6.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_7.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_8.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_9.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_10.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_11.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Left", "left_12.png")).convert_alpha()]
        self.left_masks = []
        for i in range(len(self.left)):
            self.left_masks.append(pygame.mask.from_surface(self.left[i]))
        self.right = [pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_1.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_2.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_3.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_4.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_5.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_6.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_7.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_8.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_9.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_10.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_11.png")).convert_alpha(), pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Character\Right", "right_12.png")).convert_alpha()]
        self.right_masks = []
        for i in range(len(self.right)):
            self.right_masks.append(pygame.mask.from_surface(self.right[i]))

        self.max_frames = len(self.left)
        self.image = self.left[0]
        self.mask = self.left_masks[0]
        self.rect = self.image.get_rect()
        self.rect_center = (x, y)

        self.x = x
        self.y = y
        self.is_right = False
        self.is_left = True
        self.is_jump = False
        self.velocity = 5
        self.walk_count = 0
        self.jump_count= 10
        self.latest_direction = "left"
        self.border = 1000


    def jump(self, win):
        neg = 1
        if self.jump_count >= -10:
            if self.jump_count < 0:
                neg = -1  
            self.y -= (self.jump_count*self.jump_count//1.5)//2.5 * neg
            self.jump_count -= 1
        else:
            self.is_jump = False
            self.jump_count = 10

    def movement(self, win):
        keys = pygame.key.get_pressed() #keyboard input
        if keys[pygame.K_a] and self.x > -20:
            self.is_left = True
            self.is_right = False
            self.latest_direction = "left"
            self.x -= self.velocity
        elif keys[pygame.K_d] and self.x < self.border:
            self.is_left = False
            self.is_right = True
            self.latest_direction = "right"
            self.x += self.velocity
        else:
            self.is_left = False
            self.is_right = False
        if keys[pygame.K_SPACE] and self.x > 5:
            self.is_jump = True

        if self.is_jump:    
            self.jump(win)

    def draw(self, win):
        self.hand_left = [(self.x+43, self.y+82), (self.x+41, self.y+82), (self.x+40, self.y+82), (self.x+35, self.y+77), (self.x+33, self.y+74), (self.x+30, self.y+74)]
        self.hand_right = [(self.x+45, self.y+73)]

        center_point = self.rect_center

        if self.walk_count + 1 >= 36:
            self.walk_count = 0
        if self.is_left:
            win.blit(self.left[self.walk_count//3], (self.x, self.y))
            self.rect = self.left[self.walk_count//3]
            self.walk_count += 1
            #item
            win.blit(item_flask.image, (self.hand_left[self.walk_count//6]))
        elif self.is_right:
            win.blit(self.right[self.walk_count//3], (self.x, self.y))
            self.rect = self.left[self.walk_count//3]
            self.walk_count += 1
        elif self.is_jump:
            if self.latest_direction == "left":
                win.blit(self.left[self.walk_count//3], (self.x, self.y))
                self.rect = self.left[self.walk_count//3]
            if self.latest_direction == "right":
                win.blit(self.right[self.walk_count//3], (self.x, self.y))
                self.rect = self.left[self.walk_count//3]
        else:
            if self.latest_direction == "left":
                win.blit(self.left[3], (self.x, self.y))
                self.rect = self.left[self.walk_count//3]
            if self.latest_direction == "right":
                win.blit(self.right[3], (self.x, self.y))
                self.rect = self.left[self.walk_count//3]

        self.rect_center = center_point

        #pygame.draw.rect(win, (25, 25, 25), pygame.Rect(self.x+30, self.y+74, 10, 10))

avatar = character(500, 350)

class ground(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.ground = pygame.image.load(os.path.join("Desktop\Python\Games\Game Engine\Background", "ground.png")).convert_alpha()
        self.mask = pygame.mask.from_surface(self.ground)

    def draw(self, win):
        #ground
        ground_height = 502
        ground_width = 590
        win.blit(self.ground, (590/2, ground_height))

def redraw_game_window():
    event_handling(win)
    #display background                    


    background_gradient = gradients.vertical([screen_width, screen_height], (209, 77, 135, 255), (249, 175, 88, 255))
    win.blit(background_gradient, (0,0))

#COLLISION TESTING
    if pygame.sprite.spritecollide(avatar, ground, False, pygame.sprite.collide_mask):
        print(True)


    #ground
    ground.draw(win)



    #options
    if in_options:
     options(win)


    #character
    avatar.movement(win)
    avatar.draw(win)

    pygame.display.update()

run = True
while run:
    clock.tick(36) #FPS rate
    redraw_game_window()




#Mainloop End 
pygame.quit()
1
The code is using spritecollide() but not sprites. Is this intentional? - Kingsley

1 Answers

1
votes

The code is using the PyGame sprite library spritecollide() function on objects that are not PyGame sprites. If you want to make your own sprites, it would behove you to add and maintain Rect objects to allow the code to use the Rect.collide set of functions.

But it would be much easier and quicker to convert your existing sprite-like objects to formal PyGame Sprites. This means inheriting the base Sprite class, and overriding certain functions and member variables. Using the Sprite class is (IMHO) the easiest and best way forward.

class CharacterSprite( pygame.sprite.Sprite ):
    def __init__(self, x, y, image_path ):
        pygame.sprite.Sprite.__init__( self )
        self.left_frames  = []
        self.left_masks   = []
        self.right_frames = []
        self.right_masks  = []
        # Load the walking animation
        for i in range( 4 ):
            image_name = "left_%1.png" % (i)
            image_path = os.path.join( image_path, image_name )
            self.left_frames.append( pygame.image.load( image_path ).convert_alpha() )
            self.left_masks.append( pygame.mask.from_surface( self.left_frames[-1] )
            image_name = "right_%1.png" % (i)
            image_path = os.path.join( image_path, image_name )
            self.right_frames.append( pygame.image.load( image_path ).convert_alpha() )
            self.right_masks.append( pygame.mask.from_surface( self.right_frames[-1] )
        self.max_frames  = len( self.left_frames )
        self.frame_count = 0
        self.image = self.left_frames[0]
        self.mask  = self.left_masks[0]
        self.rect  = self.image.get_rect()   # position is always maintained in the sprite.rect
        self.rect.center = ( x, y )
        # Various Stats
        self.is_left    = True
        self.is_jump    = False
        self.velocity   = 5


    def update( self ):
        """ Choose the correct animation frame to show """
        centre_point = self.rect.center
        if ( self.is_left ):
            self.image   = self.left_frames[ self.frame_count ]
            self.mask    = self.left_masks[ self.frame_count ]
        else:
            self.image   = self.right_frames[ self.frame_count ]
            self.mask    = self.right_masks[ self.frame_count ]
        self.rect        = self.image.get_rect()
        self.rect.center = centre_point

    def movement( self ):
        """ handle changes of movement an the animation """
        # handle the movement and animation
        keys = pygame.key.get_pressed() #keyboard input
        if ( keys[pygame.K_d] and self.rect.x < self.border ):  # RIGHT
            if ( self.is_left ):
                # turned to the right
                self.is_left = False
                self.frame_count = 0
            else:
                # continue right
                self.frame_count += 1
                if ( frame_count >= self.max_frames ):
                    frame_count = 0
            self.x += self.velocity
        elif ( keys[pygame.K_a] and self.rect x > -20 ):   # LEFT
            if ( self.is_left ):
                # continue left
                self.frame_count += 1
                if ( frame_count >= self.max_frames ):
                    frame_count = 0
            else:
                # turned to the left
                self.is_left = True
                self.frame_count = 0
            self.x -= self.velocity

For the sake of brevity, I did not convert the jump functionality. You would also need to make similar changes for Platform.