Source Code Makeover: Square Shooter, Part 3

This is a continuation from Part 1 and Part 2, where I go through the source code of Square Shooter, an Asteroids clone, and try to redesign the code to be more readable.

Now that I’ve refactored the code, I’ll try adding three new features: multiple bullets, a shotgun powerup and a UI change to show the remaining time for each powerup.

Adding support for multiple bullets

(These changes can be seen in the github history.) (Note that I accidentally checked in a lot of other files with this check in, please ignore them.)

First, the multiple bullets. I’ve learned from the code that the GameWorld object has a bullet member that shows the current location of the single bullet in the world. It’s set to a Bullet object when there’s a bullet on the map or None when there’s no bullet anywhere. I’m going to change this to a list data structure, which can hold multiple Bullet objects (or be an empty list if there are no bullets on the map.) I’ll rename it from bullet to bullets to reflect the multiple-bullet nature.

Any place where the bullet member is set to None (to delete the bullet) will instead either be set to [] or have a del statement called on a Bullet item in the list. Any place where the bullet member was set to a Bullet object will be changed instead to have an append() method call to add a new Bullet object to the list.

Now that there can be more than one bullet on the map, we have to change the if statement that checks if the bullet has collided with a bubble to a loop that checks each bullet for colliding with a bubble. We will change this line:

if self.bullet != None and self.bullet.collides_with(b):

...to these lines:

for i in range(len(self.bullets) - 1, -1, -1):

if self.bullets[i].collides_with(b):

Note that in an earlier change, we modified the shoot_at() method of the Ship class to return a list of Bullet objects rather than just a single Bullet object. This was done in anticipation of this future multi-bullet feature. We need to modify the caller of shoot_at() to append all of the Bullet objects to the world.bullet list. That is where this code comes from:

if len(world.bullets) < 5:

world.bullets.extend(world.ship.shoot_at(x / float(MAP_WIDTH), y / float(MAP_HEIGHT)))

The extend() list method is kind of like append(), except it appends all the items in a list instead of appending just one value.

The len(world.bullets) < 5 prevents new bullets from being added if there are five or more bullets currently on the map. (You can change this value to anything you want, but I wanted to prevent the player from just spamming the map full of bullets.

Checking these changes in with the git log message, "Add support for multiple bullets."

Add a shotgun powerup

(These changes can be seen in the github history.)

The shotgun powerup will have the ship fire multiple spread-out shots each time the mouse button is clicked. To get an idea of what code will be needed for this powerup, let's look at what code implements the other (freeze, super bullet, shield) powerups:

  • A 'shotgun' "kind".
  • Code that draws the shotgun powerup icon in the Powerup class's render() function.
  • A _shotgun_timer member for the Ship class to show how much time there is left for this powerup (and set to 0 when the player doesn't have this powerup.)
  • Code that decreases _shotgun_timer over time in the update() method.
  • Methods add_shotgun() and has_shotgun() for the Ship class.

Page 1 of 3 | Next page