Source Code Makeover: Square Shooter, Part 2

UPDATE: Felix, the original author of Square Shooter, has made some comments in the comment section that are pretty insightful. I recommend you check them out.

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

All of the changes I make can be viewed on github. You can also download the original source code for Square Shooter.

Just to say this again, readability and software design have an element of subjectivity. The changes I make here aren’t necessarily the one true way for this code to be written, and may even be worse (I’m sure you’ll be able to check the comments section for people pointing out my errors.)

Note that any line numbers mentioned refer to the line numbers in the original source code.

Renaming some variables and re-spacing code

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

Grepping to see where GameWorld gets instantiated, I see that the constructor is only called once from line 528 and placed into a global variable named model. Hmmm. I get how the GameWorld is a sort of model, but since the class name is GameWorld, let’s rename the model variable to “world” by doing a find-and-replace.

Now let’s look at the init_level() method. I’m going to change line 142 from a combination if statment and block to separate lines for the if statement and block:

if (level > self.max_level):

self.max_level = level

I think this is a bit more readable. I used the combined approach for the if statement and blocks in the wrap_around() method because the blocks were almost identical to each other, having them on the same line (and lined up) makes it easier to compare each other:

def wrap_around(self):

"""Change the position of the bubble to toroidally "wrap around" if it goes off one edge of the map."""

if self.pos.x < 0: self.pos.x += 1

if self.pos.y < 0: self.pos.y += 1

if self.pos.x > 1: self.pos.x -= 1

if self.pos.y > 1: self.pos.y -= 1

In fact, I’ll do the same thing to the Ship‘s update() method for those timer decrements (and update the comment, something I forgot to do last time):

# powerups degrade over time until it reaches 0.

if self.has_shield(): self._shield_timer -= delta_t

if self.has_super_bullets(): self._super_bullet_timer -= delta_t

if self.has_freeze(): self._freeze_timer -= delta_t

The death_timer member in GameWorld is the timer that is set after the player dies but before the level is restarted. This name is a bit ambiguous, so I’ll rename it to afterdeath_timer. I’ll do the same with finish_timer, which counts down after the last bubble has been shot but before the next level begins.

That looks pretty good. I’ll just add some comments to init_level() and then check in my changes with the log message, “Formatting changes and comments to init_level().”

Added comments to the GameWorld class

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

Now let’s look at the GameWorld class’s update() method (which is separate from the Ship class’s update() method). This is a lot of code that has no comments to help summarize them. All the comments below are mine:

def update(self, delta_t):

self.handle_collisions(delta_t)

# expand the explosions and delete them once they get too big

if len(self.explosions) > 0:

if self.explosions[0].radius > 0.5:

self.explosions.pop(0)

for i in self.explosions:

i.radius += delta_t

# "age" the powerups on the map, and delete them if they get too old

if len(self.powerups) > 0:

if self.powerups[0].age > 9:

self.powerups.pop(0)

for i in self.powerups:

Page 1 of 8 | Next page