Once More, Carefully.
Python Asteroids+Invaders on GitHub
Either I was less intelligent than usual, or there is something tricky going on here. I’ll proceed with due caution.
I wanted the Sprite to take over the BitmapMaker load, so that Sprite users could just request the Sprite they need instead of grab some bitmaps and create the Sprite. With the Invaders, I tried twice and failed. Let’s do it again, Very Carefully.
class Invader:
def __init__(self, column, row, sprite):
self._score = [10, 10, 20, 20, 30][row]
self._sprite = sprite
self.column = column
self.relative_position = Vector2(INVADER_SPACING * column, -INVADER_SPACING * row)
self.image = 0
The Invaders are created by providing bitmaps, from which each one creates its own Sprite. The BitmapMaker has all the invader bitmaps in one list of six maps:
class BitmapMaker: invader10 = (0x00, 0x00, 0x39, 0x79, 0x7A, 0x6E, 0xEC, 0xFA, 0xFA, 0xEC, 0x6E, 0x7A, 0x79, 0x39, 0x00, 0x00)
invader11 = (0x00, 0x00, 0x38, 0x7A, 0x7F, 0x6D, 0xEC, 0xFA, 0xFA, 0xEC, 0x6D, 0x7F, 0x7A, 0x38, 0x00, 0x00)
invader20 = (0x00, 0x00, 0x00, 0x78, 0x1D, 0xBE, 0x6C, 0x3C, 0x3C, 0x3C, 0x6C, 0xBE, 0x1D, 0x78, 0x00, 0x00)
invader21 = (0x00, 0x00, 0x00, 0x0E, 0x18, 0xBE, 0x6D, 0x3D, 0x3C, 0x3D, 0x6D, 0xBE, 0x18, 0x0E, 0x00, 0x00)
invader30 = (0x00, 0x00, 0x00, 0x00, 0x19, 0x3A, 0x6D, 0xFA, 0xFA, 0x6D, 0x3A, 0x19, 0x00, 0x00, 0x00, 0x00)
invader31 = (0x00, 0x00, 0x00, 0x00, 0x1A, 0x3D, 0x68, 0xFC, 0xFC, 0x68, 0x3D, 0x1A, 0x00, 0x00, 0x00, 0x00)
invaders = (invader10, invader11, invader20, invader21, invader30, invader31)
...
self.invaders = [self.make_and_scale_surface(invader, scale) for invader in invaders]
Those six items are the two frames for each of the three invader types. They are passed to the invaders by this sequence, in InvaderGroup:
def create_invader_bitmaps(self):
maker = BitmapMaker.instance()
aliens = maker.invaders
alien_table = (aliens[0:2], aliens[0:2], aliens[2:4], aliens[2:4], aliens[4:])
return alien_table
def create_invaders(self, invader_table):
self.invaders = []
for x in range(55):
col = x % 11
row = x // 11
sprite = Sprite(invader_table[row])
self.invaders.append(Invader(col, row, sprite))
We see that there are two rows of type 0, two of type 2, and one of type 4. And it is definitely the case that we need to create a new Sprite for each new Invader, because the Sprite includes the location, and each Invader has a specific location.
Let’s work by intention. Last time I tried to work down through the table and that was just never going to work.
Let’s do this:
def create_invaders(self, invader_table):
self.invaders = []
for x in range(55):
col = x % 11
row = x // 11
sprite = Sprite.invader(row)
self.invaders.append(Invader(col, row, sprite))
The Sprite now needs a new class method to get a suitable pair of invader bitmaps.
@classmethod
def invader(cls, row):
start, end = ((0, 2), (0, 2), (2, 4), (2, 4), (4, 6))[row]
maps = BitmapMaker.instance().invaders
return Sprite(maps[start:end])
Now we don’t need the invader_table.
class InvaderGroup:
def __init__(self):
self.invaders = []
self.create_invaders()
self._next_invader = 0
self.current_direction = 1
self.should_reverse = False
And we can delete the method that made the table we no longer use, and a test that tested it.
We are green and good. Commit: Modify InvaderGroup not to need BitmapMaker with help from Sprite.
A bit of progress in the afternoon. See you next time!