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.