Let’s get some more modernization up in this baby. I’m sure it’s the only reason we aren’t selling millions.

We’ve made fancy ships, fancy saucers, and fancy missiles. We have asteroids, explosions, and maybe splats to do. Let’s get to it.

It seems to me that asteroids should be easy, so I’ll start there.

``````function Asteroid:draw()
if NoAsteroids then return end
pushMatrix()
pushStyle()
stroke(255)
fill(0,0,0, 0)
strokeWidth(2)
rectMode(CENTER)
translate(self.pos.x, self.pos.y)
scale(self.scale)
strokeWidth(1/self.scale)
for i,l in ipairs(self.shape) do
line(l.x, l.y, l.z, l.w)
end
popStyle()
popMatrix()
end
``````

We can remove those push/pop things. Perhaps should leave the NoAsteroids check. It would take some time to be sure we could remove it, and it is harmless enough

``````function Asteroid:draw()
if NoAsteroids then return end
stroke(255)
fill(0,0,0, 0)
strokeWidth(2)
rectMode(CENTER)
translate(self.pos.x, self.pos.y)
scale(self.scale)
strokeWidth(1/self.scale)
for i,l in ipairs(self.shape) do
line(l.x, l.y, l.z, l.w)
end
end
``````

It’s worth a glance at asteroid creation as well:

``````function Asteroid:init(pos, size)
self.pos = pos or vec2(math.random(WIDTH), math.random(HEIGHT))
self.scale = size or 16
self.shape = Rocks[math.random(1,4)]
local angle = math.random()*2*math.pi
self.step = vec2(Vel,0):rotate(angle)
end
``````

Note that the asteroid gets created with one of the four official asteroid shapes. We could do something like that for Fancy mode as well, if we had more than one asteroid shape. Right now, we have zero. As things are built now, as soon as you flick the Fancy switch, everything cuts over to the appropriate style. So we might not want to make a final decision here anyway.

But it’s good to know.

Now for drawFancy …

``````function Asteroid:drawFancy()
if NoAsteroids then return end
translate(self.pos.x, self.pos.y)
sprite(asset.builtin.Space_Art.Asteroid_Large)
end
``````

As before, while we wait for the graphic designers to come back with marvelous asteroids, we’re plugging in these “clip art’ ones, so that the code will be ready for the real art. Which brings up an organizational issue that we should talk about. I’ve made a note to mention it in the summary. Let’s run this and see how our asteroid looks:

Well, those are ugly and evocative aren’t they? That should light a fire under those graphics designers.

The way the code is now, they won’t change size when they split:

They could at least have the decency to get smaller. That’s what the scale value is in the `draw` code. It starts at 16 and goes 16, 8, 2. I’ll patch in a guess and we’ll scale it by eye if need be.

``````function Asteroid:drawFancy()
if NoAsteroids then return end
translate(self.pos.x, self.pos.y)
scale(self.scale/16)
sprite(asset.builtin.Space_Art.Asteroid_Large)
end
``````

They do resize nicely. I wonder, though, how their scale corresponds to their kill distance. We’d like it to be pretty close. Let’s draw a circle around each one temporarily, to see what kill distance looks like:

That’s not bad, we’ll go with it. I think we’re done here Commit: Fancy asteroids.

## Explosion

I think we’d better work on the explosion of the ship. That line drawing is cute but we need something more up to the times. Let’s see how that works, it’s a bit interesting.

Explosion is just a factory that creates Fragments that fly about:

``````Explosion = class()

function Explosion:init(ship)
local pos = ship.pos
for i = 1,5 do
local f = Fragment(pos, i==1)
end
end

-- Fragment
-- RJ 20200612 from Spacewar

Fragment = class()

function Fragment:init(pos, guy)
self.pos = pos
self.color = 255
self.frag = not guy
self.base = vec2(0,0)
self.ang= math.random(360)
self.step = vec2(2.0*math.random(),0):rotate(self.ang)
self.spin = math.random(9)
self.ds = 8*math.random()
self.life = 4
end
``````

There’s only one specialized fragment now, the one that looks like a little spaceman, the “guy”. (No offense intended to any space women, but I couldn’t bear to cast one into space.) I think we’d like all our new fragments to be different, so first let’s change the logic a bit. Instead of passing in a boolean to fragment, let’s pass frag number and use that in draw to decide what to draw:

``````function Explosion:init(ship)
local pos = ship.pos
for i = 1,5 do
local f = Fragment(pos, i)
end
end

function Fragment:init(pos, frag)
self.pos = pos
self.color = 255
self.frag = frag
self.base = vec2(0,0)
self.ang= math.random(360)
self.step = vec2(2.0*math.random(),0):rotate(self.ang)
self.spin = math.random(9)
self.ds = 8*math.random()
self.life = 4
end

function Fragment:draw()
self.life = self.life - DeltaTime
if self.life < 0 then
U:deleteIndestructible(self)
end
pushStyle()
pushMatrix()
stroke(self.color)
strokeWidth(2)
noFill()
translate(self.pos.x, self.pos.y)
rotate(self.spin)
scale(1)
if self.frag ~= 1 then
line(-10,0,10,0)
line(10,0,5,13)
else
scale(3)
strokeWidth(1)
ellipse(0,5,8)
line(0,3,0,-2)
line(-4,2,4,2)
line(0,-2,-3,-5)
line(0,-2,3,-5)
end
popMatrix()
popStyle()
end
``````

That works just fine. Now for drawFancy:

``````function Fragment:drawFancy()
translate(self.pos.x, self.pos.y)
rotate(self.spin)
sprite(FragArt[self.frag])
end
``````

My cunning plan is to have a table called FragArt with space junk in it, drawn from these bits:

``````local FragArt = {asset.builtin.Space_Art.Part_Red_Hull_2,
asset.builtin.Space_Art.Part_Red_Wing_1,
asset.builtin.Space_Art.Part_Red_Wing_2,
asset.builtin.Space_Art.Part_Red_Wing_3,
asset.builtin.Space_Art.Part_Red_Wing_4,}

function Fragment:drawFancy()
translate(self.pos.x, self.pos.y)
rotate(self.spin)
sprite(FragArt[self.frag])
end
``````

This shows up with two problems:

The pieces are quite a bit too large, and they don’t go away. Note in `draw` that the timing is done there, manually, rather than with tweens. We’ll have to duplicate that, and scale down to about 1/3. And I think I’ll replace one of the wing parts with something else, they look too much alike.

``````local FragArt = {asset.builtin.Space_Art.Part_Red_Hull_2,
asset.builtin.Space_Art.Part_Red_Wing_1,
asset.builtin.Space_Art.Part_Red_Wing_2,
asset.builtin.Space_Art.Part_Red_Hull_3,
asset.builtin.Space_Art.Part_Red_Wing_4,}

function Fragment:drawFancy()
self.life = self.life - DeltaTime
if self.life < 0 then
U:deleteIndestructible(self)
end
translate(self.pos.x, self.pos.y)
scale(0.33)
rotate(self.spin)
sprite(FragArt[self.frag])
end
``````

That looks really good. I think we’ll leave the splats as they are. But that background, so 1970s. What can we do about that?

## Background

We have one immobile indestructible object now, the Score, Perfect place to display a background. Right now, II think it doesn’t have a `drawFancy`, but we can fix that right up:

``````function Score:drawFancy()
pushMatrix()
pushStyle()
self:draw()
popStyle()
popMatrix()
translate(WIDTH/2,HEIGHT/2)
scale(WIDTH/137,HEIGHT/91)
sprite(asset.milkyway)
end
``````

The scale bit is there because my graphic, milkyway, is 137x91, so that scales it to full screen. It nearly works as intended:

There are two issues. First, the background doesn’t show up in attract mode, no surprise, because score doesn’t show up there, so we should move the background to the draw in Main.

Second, the asteroids and buttons show up on a black rectangular background, which isn’t very pretty. I’m not sure what’s causing that.

I’ve reverted the code, reinstalled the `Score;drawFancy`, and played with the order of drawing. I am fairly sure there is a problem in Codea with the z-order (back to front) of drawing sprites, and/or with the setting up of assets. I’ve been unable to duplicate the issue in a small program but will continue that i hopes of generating a bug report.

With the current drawing order, things look good:

I’ve lost the new fragment code in the reversion and so far the fancy background doesn’t show up in attract mode. I’ll commit this “Fancy background in game play”, and then work on fragments and attract mode.

I also have two failing unit tests, which I’d best go after first.

``````12: Ship added to objects -- Actual: 0, Expected: 1
27: Small Saucer shoots at 20/20 accuracy -- Actual: 0, Expected: 999
``````

Curious.

``````        _:test("Ship added to objects", function()
_:expect(countObjects()).is(0)
Ship()
_:expect(countObjects()).is(1)
end)
``````

Here we need to use our new code to make a registered instance. I must not have noticed this yesterday.

This test has the same problem:

``````        _:test("Small Saucer shoots at 20/20 accuracy", function()
U = FakeUniverse()
local score = Score(3)
local ship = Ship(vec2(400,800))
local saucer = Saucer(vec2(500,800))
_:expect(saucer.size).is(0.5, 0.0001)
local count = 0
for i = 1,1000 do
local m = saucer:fireMissile()
if m.step.y < 0.001 and m.step.y > -0.001 then
count = count + 1
end
end
saucer:dieQuietly()
_:expect(count).is(999)
end)
``````

Changed tests now run. Commit: use Ship:makeRegisteredInstance in tests.

## Explosion Again

I “should” be able to just paste the explosion code back in. Should have committed it when it worked.

With the initial code in place:

``````function Explosion:init(ship)
local pos = ship.pos
for i = 1,5 do
local f = Fragment(pos, i)
end
end

function Fragment:init(pos, frag)
self.pos = pos
self.color = 255
self.frag = frag
self.base = vec2(0,0)
self.ang= math.random(360)
self.step = vec2(2.0*math.random(),0):rotate(self.ang)
self.spin = math.random(9)
self.ds = 8*math.random()
self.life = 4
end

function Fragment:draw()
self.life = self.life - DeltaTime
if self.life < 0 then
U:deleteIndestructible(self)
end
pushStyle()
pushMatrix()
stroke(self.color)
strokeWidth(2)
noFill()
translate(self.pos.x, self.pos.y)
rotate(self.spin)
scale(1)
if self.frag ~= 1 then
line(-10,0,10,0)
line(10,0,5,13)
else
scale(3)
strokeWidth(1)
ellipse(0,5,8)
line(0,3,0,-2)
line(-4,2,4,2)
line(0,-2,-3,-5)
line(0,-2,3,-5)
end
popMatrix()
popStyle()
end
``````

I’m seeing an odd thing: not all the fragments show up all the time. Sometimes there are three, sometimes four or five. Sometimes none. That’s mysterious. At a guess, they’re behind the background sprite.

This is grotesque hackery, but it works:

``````function Fragment:draw()
self.life = self.life - DeltaTime
if self.life < 0 then
U:deleteIndestructible(self)
end
stroke(self.color)
strokeWidth(2)
noFill()
translate(self.pos.x, self.pos.y)
rotate(self.spin)
scale(1)
zLevel(1) -- < --- in front of everything
if self.frag ~= 1 then
line(-10,0,10,0)
line(10,0,5,13)
else
scale(3)
strokeWidth(1)
ellipse(0,5,8)
line(0,3,0,-2)
line(-4,2,4,2)
line(0,-2,-3,-5)
line(0,-2,3,-5)
end
end
``````

I’ve not messed with zLevel yet but the default is zero, so one is in front, and now the line fragments are visible. Let’s put in the fancy draw again.

With that in, I had the same problem, the fragments not showing up, and I applied the same hammer, setting zLevel to one. I do not like that, Sam I Am. But it works.

Commit: fancy fragments.

## Background Some More

The attract mode doesn’t show the fancy background. That’s because right now only Score shows it. Let’s try showing it early on in the overall process, in attract mode.’

I’m treading lightly now, because I really don’t understand what was making the sprites not be transparent.

My first attempt made the asteroids not appear at all. That was because my background sprite needs to be drawn first, before anything else. So now I have this:

``````function draw()
if U.attractMode then
pushMatrix()
pushStyle()
if Fancy then
translate(WIDTH/2,HEIGHT/2)
scale(WIDTH/137,HEIGHT/91)
sprite(asset.milkyway)
popStyle()
popMatrix()
pushMatrix()
pushStyle()
end
fontSize(50)
fill(255,255,255, 128)
text("TOUCH SCREEN TO START", WIDTH/2, HEIGHT/4)
text(Console, WIDTH/2, HEIGHT - 200)
popStyle()
popMatrix()
end
U:draw(ElapsedTime)
end
``````

That properly displays the background in attract mode and during game play. Commit: fancy background in attract mode.

## Summing Up

### Overall Good Result

The game now has a thoroughly modern (1980s) look as opposed to its earlier 1970s look:

That’s what we set out to accomplish on Monday, and here on Tuesday it’s all in place.

That’s a good thing.

### Some Confusion

There is definitely something going on with drawing order and the transparency of assets. I think it may relate to the state of the background when the asset is accessed, and Im sure that using the zLevel capability is fraught. I think the only place I’m using it now is in the fragments, and my guess is that sometimes the background from Score is drawn before the fragments, due to the randomness of loops over the object collections.

I suspect that if I can set up the sprite background first thing in the system draw, I can remove it from Score and probably remove the zLevel stuff. We’ll try that anon.

### Organizing the art

I noticed an issue with organizing the art files. When the graphics designers come back with their lovely new sprites, we’ll have to install them by searching out the `sprite()` calls, and we’ll have to do something special about the Fragments, since they look up their sprite at run time.

Probably we should bring all the asset files together in some object or class and access them by an abstract name like “ShipArt” or “Fragment1”.

That’s for another day. Right now, after a somewhat confusing chasing of my tail on sprite backgrounds, we have a lovely new version in less than a full time day’s worth of work.