Group all the sprites together with Pygame

In the last tutorial we have created two sprite objects and then render it on the screen with the help of the GameSprite module, in this tutorial we will further modify the GameSprite module so it can be used by the pygame.sprite.Group module later on. With the use of the pygame.sprite.Group module we can easily remove a sprite from the screen once it has overlapped with another sprite. The first thing we need to do in our today tutorial is to modify the GameSprite module so it will contain the image and the rect attributes that will be used later on by the Group module.

import pygame
from pygame.locals import *

class GameSprite(pygame.sprite.Sprite):

    # Constructor. Pass in the image file
    # and it's width and height
    def __init__(self, image, width, height):
        # Call the parent class (Sprite) constructor
        pygame.sprite.Sprite.__init__(self)

        # Create the spritesheet surface from the image file
        self.sprite = pygame.image.load(image).convert_alpha()
        
        #register the width and height of the sprite
        self.width = width
        self.height = height
        
    def setImage(self, x_sprite, y_sprite, flip, player_draw_pos): # this method will return a subsurface which represents a portion of the spritesheet
    
        #the rectangle object uses in clipping
        self.clip_rect = Rect((x_sprite, y_sprite), (self.width, self.height))
        
        self.sprite.set_clip(self.clip_rect) # clip a portion of the spritesheet with the rectangle object
        sub_surface = self.sprite.subsurface(self.sprite.get_clip()) #create sub surface
        self.image = pygame.transform.flip(sub_surface, flip, False) #self.image will be called by group
        self.rect = Rect(player_draw_pos.x, player_draw_pos.y, self.width, self.height) #self.rect will be called by group
    
    def detectCollide(self, right, player_draw_pos, enemy_draw_pos): #return a boolean value indicates whether the player has collided with enemy or not
        # create the rectangle object for both enemy and player that will be used in the collide_rect function
        self.rect = Rect(player_draw_pos.x, player_draw_pos.y, self.width, self.height)
        right.rect = Rect(enemy_draw_pos.x, enemy_draw_pos.y, right.width, right.height)
        return pygame.sprite.collide_rect(self, right)

The only method which we need to change is the getImage method, first of all we will change the name of the method to setImage because this method no longer returned the sprite surface, we have also removed the return sub surface command and include a new player_draw_pos which is the Vector2D object parameter into the method. With that new parameter we can then create the new rect attribute each time the object has moved to a new position. We have also created a new image attribute on each iteration of the pygame while loop. The Group module will need the above two attributes to blit the sprite to the screen surface.

The next thing we need to do is to modify the main module a little, this time we will remove the enemy sprite once it has overlapped with the player sprite with the remove method from the Group module.

#!/usr/bin/env python

import pygame
from pygame.locals import *
from sys import exit
from vector2d import Vector2D
from game_sprite import GameSprite
from pygame.sprite import Group

robot_sprite_sheet = 'left.png'

pygame.init()

screen = pygame.display.set_mode((640, 480), 0, 32) #return a screen surface with desire screen size
pygame.display.set_caption("Pygame Demo")

w, h = 64, 64 # width and height of the sprite
sprite_counter = 0 # initialize the sprite_counter
game_frame = 0 # initialize the game_frame counter

#direction control for player
flip = False

#direction control for enemy
face_left = True

clock = pygame.time.Clock() # initialize the clock object
player_pos = Vector2D(30, 240) # initial position of the player sprite
enemy_pos = Vector2D(450, 240) # initial position of the enemy sprite
game_speed = 70. # speed per second

#create the player sprite 
game_sprite = GameSprite(robot_sprite_sheet, w, h)

#the enemy sprite object
game_sprite_two = GameSprite(robot_sprite_sheet, w, h)

#create new sprite group
sprite_group = Group(game_sprite)
sprite_group.add(game_sprite_two)

while True:
   
    for event in pygame.event.get():
        
        if event.type == QUIT:
            exit()
            
    v = Vector2D(0., 0.) # reset the speed to zero on each pass
    
    pressed_keys = pygame.key.get_pressed() # get all the keys from key-press events 
        
    if pressed_keys[K_LEFT]: 
        flip = True # if the user pressed the left arrow key then makes the sprite object facing left
        v = Vector2D(-1., 0.) # move the object one unit to the left
        
    elif pressed_keys[K_RIGHT]:
        flip = False # if the user pressed the right arrow key then makes the sprite object facing right
        v = Vector2D(1., 0.) # move the object one unit to the right
            
    screen.fill([255,255,255])
    
    player_draw_pos = Vector2D(player_pos.x-w/2, player_pos.y-h/2)#set the new player position
    game_sprite.setImage(sprite_counter * 64, 0, flip, player_draw_pos) # set the new player sprite surface
    #screen.blit(game_sprite_surface, player_draw_pos)
    
    enemy_draw_pos = Vector2D(enemy_pos.x-w/2, enemy_pos.y-h/2)#set the new enemy position
    if(sprite_group.has(game_sprite_two)):
        game_sprite_two.setImage(sprite_counter * 64, 0, face_left, enemy_draw_pos) # set the new enemy sprite surface
    #screen.blit(game_sprite_surface_two, enemy_draw_pos)
    
    #blit the sprites to the pygame screen within group
    sprite_group.draw(screen)
    
    #increase the sprite counter after x numbers of frame
    if(game_frame % 50 == 0):
        
        sprite_counter += 1
        
        if(sprite_counter > 5):
            sprite_counter = 0
            
    game_frame += 1
    
    time_passed = clock.tick() #delta time for each frame
    time_passed_seconds = time_passed / 1000.0
       
    #set the player's new position after each frame
    player_pos+= v * game_speed * time_passed_seconds   
    
    #if the enemy collides with the player, remove the enemy, else continue to move left
    if(sprite_group.has(game_sprite_two)):
        if(game_sprite.detectCollide(game_sprite_two, player_draw_pos, enemy_draw_pos)):
            sprite_group.remove(game_sprite_two)
        
        elif(face_left == True):
            enemy_pos += Vector2D(-1., 0.) * game_speed * time_passed_seconds 
    
    pygame.display.flip()

With the introduction of the Group module into our game we can now easily add or remove sprite into or from one group and blit those sprites to the screen surface with just one line of code.