Source Code Makeover: Square Shooter, Part 3

These changes are pretty much just copying the code used for the other powerups. We'll need to add some custom code to the shoot_at() method so that it returns multiple bullets if has_shotgun() returns True. Here's the new code that does this (and also replaces the previous code that only created a single Bullet object.)

bullets = []

for i in range(5):

b = Bullet()

b.pos.copy(self.pos);

b.speed.x = x * 3

b.speed.y = y * 3

# Help out the poor sods who click on their

# own ship and get stuck with a non-moving

# bullet. (2009-11-14)

if abs(x) < 0.1 and abs(y) < 0.1:

b.speed.x *= 30

b.speed.y *= 30

if not self.has_shotgun():

return [b] # just return the one bullet

b.speed.x += random.uniform(-0.15, 0.15)

b.speed.y += random.uniform(-0.15, 0.15)

bullets.append(b)

return bullets

Note that even though we loop 5 times, if has_shotgun() returns False, we just return the first Bullet object in a single-item list (since the caller is expecting a list of Bullet objects, not just a single Bullet object value.) Otherwise, a list of 5 Bullet objects will be returned.

Also notice that the return statement for the non-shotgun case occurs before this code:

b.speed.x += random.uniform(-0.15, 0.15)

b.speed.y += random.uniform(-0.15, 0.15)

This code alters the trajectory of the bullet very slightly. This is what causes the "spread" of bullets.

I'll check in this code with the log message, "Added the Shotgun powerup"

I'm going to make a slight improvement to the powerup icon for the shotgun blast by adding a couple lines so the icon looks more like the spread: Checked in with message, "Improved the shotgun powerup drawing"

Adding powerup timer UI

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

One of the things that struck me when I first started playing Square Shooter was that I didn't know what the power ups did at all. They just had these abstract looking icons, but I didn't really know which was which or what they did (the freeze was obvious, but square around my ship didn't obviously seem like a shield.)

Also, I'm not sure how long these power ups will last. In this change, we add a User Interface that displays the powerups we have as well as how much longer we will have them. In the green sidebar, we'll add the name of the powerup along with how many seconds it has remaining.

First, we'll need to change the has_shield(), has_super_bullets(), and has_freeze() methods to return the amount of time they have left, rather than a simple boolean. WARNING! You should always be careful when changing either the parameters or the return value of a function. Anything that calls these functions might break. Be sure to do extensive testing before checking in this code.

This isn't too much of a concern for our case, because a zero value returned by these methods will be equivalent to False and a non-zero value will be equivalent to True. And also, this code is not part of a library that 3rd parties depend on. (Imagine how many people would have to change their code if Pygame decided to rename pygame.Rect to pygame.Rectangle.)

Just to make sure that a negative value is returned from these methods (a negative float value is non-zero, so it will be equivalent to True), we will also add a check for a negative value and return 0 in that case.

Here's the code that we add to the GameScreen class's render() method (which draws the other parts of the green sidebar):

text_y = 48 * 6

if self.world.ship:

powerup_status = ((self.world.ship.has_shield(), 'Shield ' + str(int(self.world.ship.has_shield()))),

(self.world.ship.has_super_bullets(), 'Super Bullet ' + str(int(self.world.ship.has_super_bullets()))),

(self.world.ship.has_freeze(), 'Freeze ' + str(int(self.world.ship.has_freeze()))),

(self.world.ship.has_shotgun(), 'Shotgun ' + str(int(self.world.ship.has_shotgun()))))

for seconds_left, text in powerup_status:

if seconds_left:

text = self.msg_font.render(text, False, BLACK)

self.screen.blit(text, (MAP_WIDTH + 20, text_y))

text_y += 25

First, we create a data structure that is a tuple of tuples which hold the number of seconds remaining for the powerup (so we can tell if the powerup is active or not) and a string with the powerup's name and the number of (whole) seconds left for it.

Page 2 of 3 | Previous page | Next page