
I finished the last post and got super excited to just keep going today.
To recap what we have done so far, we have created our
- space environment and background,
- loaded our player’s spaceship,
- added movement,
- and made two different kinds of enemies for our player, asteroids and enemy space fighters.
Let’s get right into the next tutorial and finally give our player a way to fight back!
In today’s tutorial, we are first going to create the bullet class for our player, then detect and handle the many different types of collisions we have going on in our game.
THE CODE FOR THIS GAME CAN BE FOUND ON GITHUB.
Player Attack
In the last tutorial, we gave our enemy lasers to fire at our player’s ship. Now we need to return fire by creating lasers for our own player. These methods are very similar to last time, with a few changes.
1 2 |
# load bullet image bullet_img = pygame.image.load('images/laser_red.png').convert() |
In our main method, we need to load the red laser image from the images folder.
Then, we need to create the Bullet class which is very similar to the EnemyBullet class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Bullet(pygame.sprite.Sprite): '''create Bullet class''' def __init__(self, bullet_image, x, y): super().__init__() # scale bullet size self.image = pygame.transform.scale(bullet_image, (8, 23)) self.image.set_colorkey(BLACK) self.rect = self.image.get_rect() # bullet position is according the player position self.rect.centerx = x self.rect.bottom = y self.speedy = -15 def update(self): '''update bullet''' self.rect.y += self.speedy # if bullet goes off top of window, destroy it if self.rect.bottom < 35: self.kill() |
Here we create the Bullet class, resize the image in line 6, and spawn the bullet according to the x and y positions of the player. Then in the update function, we move the bullet up in the y direction, hence self.speedy = -15. Finally, if the bullet reaches where y is less than 35, the bullet is removed. 35 is used because later at the top of the screen we will create a display that will cover that region.
When we created the enemy ship, a bullet is fired based upon the enemy’s location and the last time a shot was fired. For our player, we need to assign the action to a key, the ‘space’ key, and check if the key is pressed.
In the update method of our Player class, add event handling for the ‘space’ key.
1 2 3 |
# fire weapons by holding the 'space' key if keys[pygame.K_SPACE] and not(self.rect.top > WINDOWHEIGHT): self.shoot() |
You will notice that the checking for the event also checks if the player is outside of the screen. This is because later if our player dies, we will remove it temporarily outside of our view and we don’t want to be able to still shoot if we are ‘dead’.
1 2 3 4 5 6 7 8 |
def shoot(self): '''fire bullets''' current_time = pygame.time.get_ticks() if current_time - self.last_shot > self.shoot_delay: self.last_shot = current_time bullet = Bullet(self.bullet_image, self.rect.centerx, self.rect.top) self.sprites.add(bullet) self.bullets.add(bullet) |
This is our basic shoot function. We will be modifying it quite a bit in the future. We check if the player has fired a bullet, and cause a delay on the next bullet that can be fired. That’s about it for firing bullets for now.
Collision Detection
First, let’s discuss what collision detection is. Then, we will show the code on how to check for our various collisions.
A collision is similar to what you would consider in a real-life car crash. Two cars can’t occupy the same space and so we need to consider this in our game, as well. Since, Pygame loads the objects one on top of the other, objects will overlap when they occupy the same space in our window. However to create more realistic gameplay, we need to check if the rectangles of our objects are overlapping, or have collided. This is useful for checking if our bullet hit the enemy, if the enemy hit our player, handling if two characters collide and what should happen. Do they bounce off each other? Or blow up? Or maybe just lose some health.
For this point of the game, we need to check if 1) the player bullets hit an asteroid, 2) the player bullets hit an enemy ship, 3) the enemy hits our player, 4) the enemy bullets hit our player or 5) or an asteroid hits our player.
Luckily that is fairly simple in Pygame and we can use:
1 |
pygame.sprite.groupcollide(Group1, Group2, dokill1=False, dokill2=False, collided=None) |
for collision between two lists of objects. Set dokill 1 or 2 to True for sprites to be removed from their respective Group if they have collided. The final argument, collided, is used to check if two objects are colliding. Or use:
1 |
pygame.sprite.spritecollide(Sprite, Group, dokill=False, collided=None) |
to check for collisions between a single object and a group of objects. Set dokill=True for sprites to removed from the Group if they have collided.
Let’s begin by looking at collisions between the player’s bullets and the asteroids. NOTE: These methods are the simplified versions and will be added to in future posts.
1 2 3 4 5 6 7 |
# check if a bullet hit an asteroid asteroid_hit = pygame.sprite.groupcollide(asteroids, bullets, True, pygame.sprite.collide_circle) # when asteroids are destroyed, spawn new asteroids for hit in asteroid_hit: new_asteroid = Asteroid(asteroid_images, all_active_sprites, asteroids) all_active_sprites.add(new_asteroid) asteroids.add(new_asteroid) |
As the player’s bullets are moving in the game window, if the rect of the bullet collides with the rect of an asteroid, the bullet and the asteroid are removed using collision detection. A new Asteroid object is then created above the game window so that more asteroids will continue to come in lines 5 – 8.
1 2 3 4 5 6 7 |
# check if a bullet hit an enemy ship enemy_hit = pygame.sprite.groupcollide(enemy_ships, bullets, True, pygame.sprite.collide_circle) # when asteroids are destroyed, spawn new asteroids for hit in enemy_hit: new_ship = EnemyShip(enemy_img, enemy_bullet_img, all_active_sprites, enemy_bullets) all_active_sprites.add(new_ship) enemy_ships.add(new_ship) |
This is similar to above with the asteroids, but now we are checking if our bullets hit the enemy players, and replacing the ships if they do collide.
1 2 3 4 5 6 |
# check if enemy bullet hit player player_hit_by_bullet = pygame.sprite.spritecollide(player, enemy_bullets, True) # if player is hit for hit in player_hit_by_bullet: print("Player hit") |
Here, we are checking if the bullets from the enemy have hit our player. Since we haven’t created the score or explosion animations yet, we will just print out if the player was hit or not to the Terminal window.
Finally, we need to check for collisions between our player and asteroids and between our player and enemy ships.
1 2 3 4 5 6 7 |
# check for collisions between asteroids and player player_hit = pygame.sprite.spritecollide(player, asteroids, True) # if player is hit for hit in player_hit: new_asteroid = Asteroid(asteroid_images, all_active_sprites, asteroids) all_active_sprites.add(new_asteroid) asteroids.add(new_asteroid)<br> |
For now, we will just reset the asteroids if they collide with our space ship. And similarly for collisions from enemy ships.
1 2 3 4 5 |
# if player is hit by enemy ship for hit in player_hit_by_ship: new_ship = EnemyShip(enemy_img, enemy_bullet_img, all_active_sprites, enemy_bullets) all_active_sprites.add(new_ship) enemy_ships.add(new_ship) |
This what it will looks like after the collision detection is set up.

Summary
We have looked at how to check for collisions between many different objects in our game.
Next time, we are going to talk about and show how to load animations in Pygame to add more realism to our game with explosion animations.
Please leave a comment with questions or comments to help us improve your experience here at Red Huli.