I’ve moved the good iPad into the TV room and while waiting for our Sunday festivities to start, I’ll set up the project’s bitmap assets.

I really didn’t remember how to move the bitmaps from where they were created to the current project assets. Fortunately, I had written that up when I did the sound assets for Asteroids, so I copied the process. I’ll describe it here as well.

The bitmaps were created by the program MakeInvaders. I’ll put a copy of that in an appendix.

Moving them into our project turns out to be easy. I pasted in a temporary access to a sprite:

function setup()
    local bits = sprite(xxx,0,0)
    runTests()
    createShields()
    TheArmy = Army()
    setupGunner()
    Missile = {v=0, p=vec2(0,0)}
    invaderNumber = 1
end

When you touch the xxx, the Codea asset browser pops up:

asset browser

From that you can tap any of the sources. The one called Dropbox is a shared folder among all the Codea programs, and it’s the one where MakeInvaders has put the ones we want. We tap Dropbox and:

dropbox

And there they all are. (That gray dot is the touchpad cursor. It shows up randomly when I accidentally touch the touchpad. You can safely ignore it.)

Now one taps the Edit up at the top. Edit changes to Done and we can begin selecting:

selecting

And we can scroll down until we’ve selected everything we might want:

all-selected

Press the “AddTo” down at the bottom and you get this:

addTo

Select the Invaders project assets, and they’ll all be copied in. Thereafter, when you touch anything that wants an asset, you can use the built in ones:

select-project

And there they all are:

all there

Far from obvious, but quite easy.

Just for fun, I’ll see if I can make it display the gunner asset, which is named “player” if I’m not mistaken. Here’s our draw now:

function drawGunner()
    pushMatrix()
    pushStyle()
    if Gunner.alive then
        stroke(255)
        fill(255)
    else
        stroke(255,0,0)
        fill(255,0,0)
        Gunner.count = Gunner.count - 1
        if Gunner.count <= 0 then Gunner.alive = true end
    end
    rect(Gunner.pos.x, Gunner.pos.y, 16,8)
    stroke(255,0,0)
    fill(255,0,0)
    popStyle()
    popMatrix()
    Gunner.pos = Gunner.pos + GunMove
end

We just need to use the sprite, I reckon. It turns out that the name is “play”, not “player” for some reason. I replace the call to rect with this:

    sprite(asset.play,Gunner.pos.x,Gunner.pos.y)

And Lo! the sprite appears:

player  sprite

It doesn’t change color when hit, however, because that’s not done with stroke and fill, but with tint, if I’m not mistaken.

function drawGunner()
    pushMatrix()
    pushStyle()
    if Gunner.alive then
        tint(255)
    else
        tint(255,0,0)
        Gunner.count = Gunner.count - 1
        if Gunner.count <= 0 then Gunner.alive = true end
    end
    sprite(asset.play,Gunner.pos.x,Gunner.pos.y)
    stroke(255,0,0)
    fill(255,0,0)
    popStyle()
    popMatrix()
    Gunner.pos = Gunner.pos + GunMove
end

And now the sprite turns red when hit. I didn’t have to wait too long to catch this snapshot:

sprite red

It’s time for Sunday brekkers, so I’ll stop here and commit (55 files, by the way, counting two sizes for the bitmaps).

Monday, Monday

Well, here we are on Monday at 0723, and probably the thing to do is to add in the rest of the bitmaps.

It seems to me that the shields should be easy, because we are already making copies:

function createShields()
    local img = image(22,16)
    for row = 1,16 do
        for col = 1,22 do
            img:set(col,row,0,255,0)
        end
    end
    local posX = 34+11
    local posY = 100
    Shields = {}
    for s = 1,4 do
        local entry = {img=img:copy(), pos=vec2(posX,posY), count=0, alive=true}
        table.insert(Shields,entry)
        posX = posX + 22 + 23
    end
end

I’m slightly irritated that this code makes four copies rather than using the original plus three copies, but I don’t know a really super way to deal with that. Hmm, I’ve just thought of one, but we’ll deal with that later. For now, let’s just replace that procedural bitmap with our official one.

I’m not exactly sure how to do that. My guess is that I should assign an asset to the image.

This attempt fails:

function createShields()
    local img = asset.shield
    local posX = 34+11
    local posY = 100
    Shields = {}
    for s = 1,4 do
        local entry = {img=img:copy(), pos=vec2(posX,posY), count=0, alive=true}
        table.insert(Shields,entry)
        posX = posX + 22 + 23
    end
end

The message is:

Main:52: attempt to call a nil value (method 'copy')
stack traceback:
	Main:52: in function 'createShields'
	Main:6: in function 'setup'

So whatever came back from that assignment must not be a legitimate image. Time to RTFM. (Read the Fine Manual, of course.) Ah. We want ‘readImage`:

function createShields()
    local img = readImage(asset.shield)
    local posX = 34+11
    local posY = 100
    Shields = {}
    for s = 1,4 do
        local entry = {img=img:copy(), pos=vec2(posX,posY), count=0, alive=true}
        table.insert(Shields,entry)
        posX = posX + 22 + 23
    end
end

That does the trick, and now we have individual shields:

four shields

While we’re at it, let’s move those guys down until they’re right above the player, then we’ll commit. We’ll set posY to just a bit above the player, which is at Y = 10. He’s 16 high, so his top is at 18, so let’s try 24.

shields24

That height looks about right, and since the right edge of one of our guys is right on the center, we should probably find that removing that +11 will center them nicely:

function createShields()
    local img = readImage(asset.shield)
    local posX = 34
    local posY = 24
    Shields = {}
    for s = 1,4 do
        local entry = {img=img:copy(), pos=vec2(posX,posY), count=0, alive=true}
        table.insert(Shields,entry)
        posX = posX + 22 + 23
    end
end

And that looks good:

shields ok

Let’s commit: shields use real asset.

Digression? The Copy Thing

I want to reflect a moment on the fact that we read the shield asset and then make four copies of it, wasting one since we only need four. That’s certainly not as efficient as making only three copies. Why didn’t I “fix” that? There are a few reasons.

First, since the inefficiency is during setup, it’s not terribly important. And since the extra is created inside a function, it’ll get garbage collected if the storage is ever needed. Overall, it’s not a big issue.

Second, I don’t know a simple and easy pattern for doing it. There’ll be some kind of if statement inside the loop, and it seems to me that it’ll be messy.

Third, I was on another mission, to use the stored image, and diverting to fix this not very important thing would have detracted from what I was doing, and quite possibly confused me. Now, since I was really just replacing a few lines of code with one, the chances of confusion were pretty low–but remember who you’re dealing with here–but the principle of not going off plan is one that I try to cultivate.

Why not fix it now, though, since we’ve completed the task? That’s a fair question, since we’re supposed to clean up our code when we make a thing work, so this is the right moment to do it.

However: the code is clean. It just isn’t quite as efficient as it might be. “Make it fast” is the third step in the mantra “Make it work, make it right, make it fast.” I was admonished by Kent Beck Lo! these many years ago, to optimize code only when a performance measurement showed that the optimization was needed. I took that advice to heart after I had spent a day optimizing something and it had no actual effect on the system’s run time. OK, maybe that happened a few times.

So, no. This is clean, and it’s fast enough.

Let’s move on.

Invaders? or Missiles?

We have at least the invader sprites, and the missile sprites left to do. I think the invaders offer more “business value”, because they improve the game a lot. There’s nothing all that deeply menacing about a few rows of encroaching evil squares.

We did a spike for this, and I’m going to have a look at it now.

Some of the code isn’t bad, but I’ve decided not to include it here. Instead, I’ll put it down in another appendix and use it for reference to write what we need in our current structure.

In our current scheme we have a class Army that contains all the instance of the class Invader. The init looks like this:

function Army:init()
    self.invaders = {}
    for row = 1,5 do
        for col = 1,11 do
            local p = vec2(col*16, 256-row*16)
            table.insert(self.invaders, 1, Invader(p))
        end
    end
    self.invaderNumber = 1
    self.overTheEdge = false
    self.motion = vec2(2,0)
    self.updateDelay = 1/65 -- less than 1/60th by a bit
    self:resetTimeToUpdate()
    self.bombs = {}
end

A decent pattern seems to me to be to fetch the sprite images here, and to give each invader the info she needs to draw herself. In the spike, I gave each one just her two basic images, based on the row she’s in. So let’s try that … this will read them in:

function Army:init()
    local vader11 = readImage(asset.documents.Dropbox.inv11)
    local vader12 = readImage(asset.documents.Dropbox.inv12)
    local vader21 = readImage(asset.documents.Dropbox.inv21)
    local vader22 = readImage(asset.documents.Dropbox.inv22)
    local vader31 = readImage(asset.documents.Dropbox.inv31)
    local vader32 = readImage(asset.documents.Dropbox.inv32)
    self.invaders = {}
    self.invaders = {}
    for row = 1,5 do
        for col = 1,11 do
            local p = vec2(col*16, 256-row*16)
            table.insert(self.invaders, 1, Invader(p))
        end
    end
...

The game layout is that the first two rows get inv11 and inv12, the next two get inv21 and inv22, and the third gets inv31 and inv32.

So what if we just made a little table of pairs …

    local vaders = {
        {vader11,vader12}, {vader11,vader12},
        {vader21,vader22}, {vader21,vader22},
        {vader31,vader32}}
    }

And we give each invader her pair:

    self.invaders = {}
    for row = 1,5 do
        for col = 1,11 do
            local p = vec2(col*16, 256-row*16)
            table.insert(self.invaders, 1, Invader(p,vaders(row)))
        end
    end

Now each invader has a new parameter, her pair of sprites for her init:

function Invader:init(pos)
    self.pos = pos
    self.alive = true
end

So she’ll save those …

function Invader:init(pos, sprites)
    self.sprites = sprites
    self.pos = pos
    self.alive = true
end

And to close this out for a commit, we’ll have her just draw herself as the first one:

function Invader:draw()
    if self.alive then
        sprite(sprites[1])
    end
end

Well. I expected that to work. But I get this message:

Army:19: attempt to call a table value (local 'vaders')
stack traceback:
	Army:19: in field 'init'
	... false
    end

    setmetatable(c, mt)
    return c
end:24: in global 'Army'
	Main:7: in function 'setup'

Sounds like a parenthesis problem. Here’s that code:

            table.insert(self.invaders, 1, Invader(p,vaders(row)))

Yes, we meant this:

            table.insert(self.invaders, 1, Invader(p,vaders[row]))

We get another error, and fix this typo, a missing self:

function Invader:draw()
    if self.alive then
        sprite(self.sprites[1])
    end
end

Nothing appears now, and I suspect it’s because a sprite has to have a location.

function Invader:draw()
    if self.alive then
        sprite(self.sprites[1], self.pos.x, self.pos.y)
    end
end

And we have invaders!

invaders wrong order

They’re in the wrong rows, because we build the rows top down, not bottom up. Reorder the table:

    local vaders = {
        {vader31,vader32},
        {vader21,vader22}, {vader21,vader22},
        {vader11,vader12}, {vader11,vader12},
    }

And we’re good:

invaders good

Commit: Invaders have bitmaps, no animation.

Did you notice the trailing comma in the vaders table, after the last entry. Turns out that Lua doesn’t mind that being there, and languages that allow that are among my favorites. You might argue that allowing it invites leaving elements out by accident, but it’s my computer and I like it.

That said, full disclosure, I’m going to remove it, since it’s not needed and we’ve noticed it now.

I want to press on to animation but first I think I’ll go get a chai. Back in a half hour: 0830 … 0900. Perfect estimate.

Animation

Our little invaders have two images, indexed 1 and 2, because Lua, and we want them to switch between them on each update, if they’re alive:

function Invader:update(motion, army)
    if self.alive then
        self:possiblyDropBomb(army)
        self.pos = self.pos + motion
        if self:overEdge() then
            army:invaderOverEdge(self)
        end
        return false
    else
        return true
    end
end

So we need a new member variable that will let us do that. Because it’s easy to do one that varies between 0 and 1, we’ll do this:

function Invader:init(pos, sprites)
    self.sprites = sprites
    self.pos = pos
    self.alive = true
    self.picture = 0 -- <---
end

function Invader:draw()
    if self.alive then
        sprite(self.sprites[self.picture+1], self.pos.x, self.pos.y) -- <---
    end
end

function Invader:update(motion, army)
    if self.alive then
        self.picture = (self.picture+1)%2 -- <---
        self:possiblyDropBomb(army)
        self.pos = self.pos + motion
        if self:overEdge() then
            army:invaderOverEdge(self)
        end
        return false
    else
        return true
    end
end

At first, I initialized the picture to 1, thinking she’d update before drawing, and I wanted to get her off on the right foot, but she draws and then updates, so I changed the init to zero. In practice it makes no discernible difference.

The invaders are now animated:

animated

We can see quite a few issues in that movie. Missiles fired from the player take off from the far left, because we’ve changed origins to CORNER. Missiles don’t hit aliens when they’re hit dead on, but do hit them when the missile goes left. Same issue. Same with bombs hitting the player. We’ll deal with that in due time.

For now, commit: animated aliens.

Story done, let’s reflect:

Reflection

When Chet and I wrote about our “new approach”, which I fondly hope we never really try to do, we named it Twelve™, because XP had the dials turned to eleven and we wanted to go further.

In Twelve™, e think of the work as being done in what we called “Brackets”, nested time intervals with a beginning and an end. If you think about it for a moment, such things generally have the closing and opening brackets together:

…][ 1 ][ 2 ][ 3 ][ …

At each boundary, we look back to assess what we’ve done, and then we look forward to see what we’ll do next.

I really hope we never try to push Twelve™, as the world doesn’t really need yet another approach, but it’s an interesting way to think about things. In particular, at every work break, it’s useful to look back and look forward, to kind of lock in any discoveries or learning, and to see what has been accomplished. We might even want to tell someone about it.

So, reflecting …

The sprites for the player, the shields, and the invaders have all gone in quite readily. The invader animation was just a few lines of code, which is what we’d hope for. The update function provided the exact moment for doing the selection.

It’s worth noting that this went easily, because the ease or difficulty of doing things tells us something important about code quality and design. When our quality is high enough, and our design is close to what we need, things tend to go smoothly. When the code is not clean enough, or the design is not close enough, things go slowly.

If we pay attention, we can feel the difference between smooth and slow right away. If we do something too quick and dirty today, we can probably feel the impact tomorrow. It will be much longer before those effects show up on a projection of what will be done by a given date, but as developers we can feel the drag right away.

Today, I didn’t feel much drag. Things went in nicely.

We should give some credit to the spikes we did earlier as well. Those spikes gave us concrete experience with reading images, displaying them, and toggling between them, and that experience, plus a glance at the code we used, made today’s work fee more familiar, not so much like doing something in that horrid fumbling first-time fashion.

A good morning’s work. We’re two hours in, even though it’s only 0927, so perhaps we’ll wrap this one after thinking about what’s next.

What’s Next?

Well. Lots to choose from:

  • Aiming points are off by half an object;
  • Missiles need to be animated;
  • Aliens explode when shot;
  • Shields get gnawed away by bombs;
  • Shields get eaten by aliens if they get there;
  • Aliens still fall off the edges of the screen;
  • Aliens advance too slowly;
  • The game is supposed to have sound;
  • There’s a saucer;
  • There’s supposed to be scoring;
  • There can be two players;
  • … and surely a lot more …

Right now, I think I might work on the dimensioning, getting aliens to turn back at the right point, and to move downward more expeditiously. Or maybe just faster. Shield destruction will be fun but I think I understand what needs to be done.

I am not committing to building everything on the list. The idea here is to learn from building, and to have fun doing so. When it gets to be too little fun, or too little learning, I’ll turn my attention to something else.

One *Very Difficult Thing(, which I almost certainly won’t do, would be to use Codea’s ability to produce XCode, and to build the application to run as a native application. I’m sure I’d learn a lot, but I’m also sure it won’t be fun. I might undertake it if there was someone knowledgeable who wanted to pair with me on it.

Anyway, for now, we have some animated aliens, and all the major sprites moved in, so I’ll call it a morning.

See you next time!

Invaders.zip


Appendix: MakeInvaders

This is the program that takes the bitmaps, lightly modified from the original Space Invaders source, and puts them into Dropbox.assets, which is a common filing area having nothing at all to do with the Dropbox program you may know and love.

The commented ones are bitmaps I chose not to bring in, part of the attract mode, which I am not planning to replicate.

The save function puts each bitmap into the Codea Dropbox thingie:

function save(name, map)
    saveImage(asset.documents.Dropbox .. name, map)
end

When it runs, the program draws a scale 5 picture of all the bitmaps:

bitmaps

displayMode(STANDARD)

function setup()
    parameter.integer("sc",1,15,5)
    
--[[    
alien_upside_down_Y_1
={0x00,0x03,0x04,0x78,0x14,0x13,0x08,0x1A,0x3D,0x68,0xFC,0xFC,0x68,0x3D,0x1A,0x00}
alien_upside_down_Y_2
={0x00,0x00,0x01,0xB8,0x98,0xA0,0x1B,0x10,0xFF,0x00,0xA0,0x1B,0x00,0x00,0x00,0x00}

Splash_shooting_C
={0x00,0x10,0x00,0x0E,0x05,0x00,0x00,0x00,0x00,0x00,0x07,0xD0,0x1C,0xC8,0x9B,0x03}

Alien_Y2
={0x00,0x00,0x03,0x04,0x78,0x14,0x0B,0x19,0x3A,0x6D,0xFA,0xFA,0x6D,0x3A,0x19,0x00}
]]--

player_shot
={0x0F}
    map_player_shot = image(1,8)
    bitMap(player_shot, map_player_shot)
    save("player_shot", map_player_shot)
    

player_shot_exploding
={0x99,0x3C,0x7E,0x3D,0xBC,0x3E,0x7C,0x99}
    makeBitmap(player_shot_exploding, 8,8, "player_shot_exploding")

Alien_Exploding
={0x00,0x08,0x49,0x22,0x14,0x81,0x42,0x00,0x42,0x81,0x14,0x22,0x49,0x08,0x00,0x00}
    makeBitmap(Alien_Exploding, 16,8, "alien_exploding")

squig1
={0x44,0xAA,0x10}
    makeBitmap(squig1, 3,8, "squig1")

squig2
={0x88,0x54,0x22}
    makeBitmap(squig2, 3,8, "squig2")

squig3
={0x10,0xAA,0x44}
    makeBitmap(squig3, 3,8, "squig3")

squig4
={0x22,0x54,0x88}
    makeBitmap(squig4, 3,8, "squig4")

alien_shot_exploding
={0x4A,0x15,0xBE,0x3F,0x5E,0x25}
    makeBitmap(alien_shot_exploding, 6,8, "alien_shot_exploding")

plunger_shot_1
={0x04,0xFC,0x04}
    makeBitmap(plunger_shot_1, 3,8, "plunger1")

plunger_shot_2
={0x10,0xFC,0x10}
    makeBitmap(plunger_shot_2, 3,8, "plunger2")

plunger_shot_3
={0x20,0xFC,0x20}
    makeBitmap(plunger_shot_3, 3,8, "plunger3")

plunger_shot_4
={0x80,0xFC,0x80}
    makeBitmap(plunger_shot_4, 3,8, "plunger4")

rolling_shot_1
={0x00,0xFE,0x00}
    makeBitmap(rolling_shot_1, 3,8, "rolling1")

rolling_shot_2
={0x24,0xFE,0x12}
    makeBitmap(rolling_shot_2, 3,8, "rolling2")

rolling_shot_3
={0x00,0xFE,0x00}
    makeBitmap(rolling_shot_3, 3,8, "rolling3")

rolling_shot_4
={0x48,0xFE,0x90}
    makeBitmap(rolling_shot_4, 3,8, "rolling4")

shield
=  {0xFF,0x0F,0xFF,0x1F,0xFF,0x3F,0xFF,0x7F,  
    0xFF,0xFF,0xFC,0xFF,0xF8,0xFF,0xF0,0xFF, 
    0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,
    0xF0,0xFF,0xF0,0xFF,0xF8,0xFF,0xFC,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF,0x3F,
    0xFF,0x1F,0xFF,0x0F}
    makeBitmap2(shield, 22,16, "shield")

Saucer
={0x00,0x00,0x00,0x00,0x04,0x0C,0x1E,0x37,0x3E,0x7C,0x74,0x7E,0x7E,0x74,0x7C,0x3E,0x37,0x1E,0x0C,0x04,0x00,0x00,0x00,0x00}
    makeBitmap(Saucer, 24,8, "saucer")

saucer_exploding
={0x00,0x22,0x00,0xA5,0x40,0x08,0x98,0x3D,0xB6,0x3C,0x36,0x1D,0x10,0x48,0x62,0xB6,0x1D,0x98,0x08,0x42,0x90,0x08,0x00,0x00}
    makeBitmap(saucer_exploding, 24,8, "saucer_exploding")

    --[[
alien_with_Y
={0x60,0x10,0x0F,0x10,0x60,0x30,0x18,0x1A,0x3D,0x68,0xFC,0xFC,0x68,0x3D,0x1A,0x00}
    makeBitmap(alien_with_Y, 16,8, "alien_y")
]]--

end

function bitMap(hexString,img)
    for a=1,#hexString do
        for z=0,7 do
            if (hexString[a]>>z)&1==1 then
                img:set(a,z+1,255,255,255,255)
            end
        end 
    end   
end

function bitMap2Row(hexString,img)
    local col = 0
    for str = 1, #hexString, 2 do
        col = col + 1
        b1 = hexString[str]   -- bottom
        b2 = hexString[str+1] -- top
        for z = 0,7 do
            if (b1>>z)&1==1 then -- set bottom
                img:set(col,z+1, 255,255,255,255)
            end
            if (b2>>z)&1==1 then -- set top
                img:set(col,z+9,255,255,255,255)
            end
        end
    end   
end


function draw()
    background(0)
    noSmooth()
    fontSize(30)
    fill(255)
    text("Scale  "..sc,WIDTH/2,HEIGHT-100)
    local y
    scale(sc,sc)
    sprite(asset.documents.Dropbox.player_shot,WIDTH/2/sc,HEIGHT*.8/sc)
    sprite(asset.documents.Dropbox.player_shot_exploding,WIDTH/2/sc + 3*sc,HEIGHT*.8/sc)
    sprite(asset.documents.Dropbox.alien_exploding,WIDTH/2/sc,HEIGHT*.7/sc)
    sprite(asset.documents.Dropbox.alien_shot_exploding,WIDTH/2/sc + 3*sc,HEIGHT*.7/sc)
    y = 0.6
    sprite(asset.documents.Dropbox.plunger1,WIDTH/2/sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.plunger2,WIDTH/2/sc + 3*sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.plunger3,WIDTH/2/sc + 6*sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.plunger4,WIDTH/2/sc + 9*sc,HEIGHT*y/sc)
    y = 0.5
    sprite(asset.documents.Dropbox.rolling1,WIDTH/2/sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.rolling2,WIDTH/2/sc + 3*sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.rolling3,WIDTH/2/sc + 6*sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.rolling4,WIDTH/2/sc + 9*sc,HEIGHT*y/sc)
    y = 0.4
    sprite(asset.documents.Dropbox.squig1,WIDTH/2/sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.squig2,WIDTH/2/sc + 3*sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.squig3,WIDTH/2/sc + 6*sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.squig4,WIDTH/2/sc + 9*sc,HEIGHT*y/sc)
    y = 0.3
    sprite(asset.documents.Dropbox.saucer,WIDTH/2/sc,HEIGHT*y/sc)
    sprite(asset.documents.Dropbox.saucer_exploding,WIDTH/2/sc + 6*sc,HEIGHT*y/sc)
    y = 0.1
    sprite(asset.documents.Dropbox.shield,WIDTH/2/sc,HEIGHT*y/sc)
end

function save(name, map)
    saveImage(asset.documents.Dropbox .. name, map)
end

function makeBitmap(bits, cols, rows, name)
    local img = image(cols,rows)
    bitMap(bits, img)
    save(name, img)
end

function makeBitmap2(bits, cols, rows, name)
    local img = image(cols,rows)
    bitMap2Row(bits, img)
    save(name, img)
end

Invaders Spike

-- invadersSpike

local MyRank

function setup()
    local vader11 = readImage(asset.documents.Dropbox.inv11)
    local vader12 = readImage(asset.documents.Dropbox.inv12)
    local vader21 = readImage(asset.documents.Dropbox.inv21)
    local vader22 = readImage(asset.documents.Dropbox.inv22)
    local vader31 = readImage(asset.documents.Dropbox.inv31)
    local vader32 = readImage(asset.documents.Dropbox.inv32)
    local vaders = {vader11,vader12, vader21, vader22, vader31,vader32}
    MyRank = Rank(vaders)
end

function draw()
    MyRank:update(ElapsedTime)
    background(40, 40, 50)
    rectMode(CENTER)
    noFill()
    stroke(255)
    strokeWidth(2)
    rect(WIDTH/2, HEIGHT/2, 224*4, 256*4)
    spriteMode(CORNER)
    text(DeltaTime, 100,100)
    translate(WIDTH/2-224*2, HEIGHT/2)
    MyRank:draw()
end

Rank = class()

local Ranks;

function Rank:init(images)
    self.invaderNumber = 1
    self.updateStep = vec2(2,0)
    self.invaders = {}
    self.undone = {}
    self.reverse = false
    self:initInvaders(images)
    self.lastElapsedTime = ElapsedTime
    self.timeToUpdate = 1/60
    self.imageToggle = 1
end

local invaderOffset = {1,1, 3,3, 5}

function Rank:initInvaders(images)
    for rank = 0,4 do
        for col = 0,10 do
            local imageBase = invaderOffset[1 + rank]
            local herImages = {images[imageBase], images[imageBase+1]}
            table.insert(self.invaders, {p=vec2(col*16,rank*16), images=herImages})
        end
    end
end

function Rank:draw()
    pushMatrix()
    pushStyle()
    scale(4)
    rankEntry = self
    for i,invader in ipairs(rankEntry.invaders) do
        local pos = invader.p
        local v = invader.images[self.imageToggle]
        sprite(v, pos.x, pos.y)
    end
    popStyle()
    popMatrix()
end

function Rank:update(elapsed)
    if elapsed < self.lastElapsedTime + self.timeToUpdate then return end
    if #self.undone ~= 0 then
        local invader = self.undone[1]
        table.remove(self.undone, 1) -- costly
        invader.p =invader.p + self.updateStep
    else -- all moved
        self.imageToggle = self.imageToggle == 2 and 1 or self.imageToggle + 1
        self:copyToUndone()
        self.updateStep.y = 0
        if self.updateStep.x > 0 and self.invaders[#self.invaders].p.x > 224 - 16 then
            self.reverse = true
        elseif self.updateStep.x < 0 and self.invaders[1].p.x < 0 then
            self.reverse = true
        end
        if self.reverse then
            self.updateStep = - self.updateStep - vec2(0,2)
            self.reverse = false
        end
    end
    self.lastElapsedTime = elapsed
end

function Rank:copyToUndone()
    self.undone = table.pack(table.unpack(self.invaders))
end