I’m feeling like coding a bit this evening. So stand back.

Before I figure out something easy to do, I wanted to pass on a comment from “West” on the Codea forum. He created an Asteroids tutorial a while back, and he commented without criticism that he noticed that I went for getting the right asteroid shapes rather early on.

I did. The reason was that I had been reading the 6502 code and wondered whether I was understanding the vector code of the DVG processor and I just kind of got into it. And it was kind of interesting building that little conversion program that converted the DVG code to something I could use in Codea.

However, it’s worth noting that in the spirit of getting a program working ASAP and keeping progress going from start to finish, spending time on the asteroid shapes might not be an ideal strategy.

The point is, I’m not here to say what the ideal strategy is, nor do I claim that what I’m doing is particularly ideal, particularly “Agile”, or particularly anything. I’m here to show you what I do, what happens when I do it, and to try to point to what I believe about what I do and what happens.

What I believe might not even be true for me, much less for you. What I hope for is to give you things to think about and to use to adjust your own practice, even if it’s to say Well, I’ll never do that!

I’m trying to do reasonable things, certainly, and trying to make clear what I’m up to and why. It remains up to you to find the value.

Anyway, what shall we do tonight?

Universe

What I think I’ll do is work a bit on the Universe. In this morning’s exercise I moved some creation from Main / setup into Universe. I think I’d like to move everything in there, first from setup and then later from Main’s other components, leaving just the basic operational hooks in Main that have to be there.

Tonight I’m just thinking to deal with setup but we’ll see where we wind up. The code may lead us elsewhere. Here’s Main setup and its globals.

-- Asteroids
-- RJ 20200511

Touches = {}
Ratio = 1.0 -- draw time scaling ratio
Score = 0

function setup()
    U = Universe()
    U:createAsteroids()
    Score = 0
    --displayMode(FULLSCREEN_NO_BUTTONS)
    createButtons()
end

I believe that Ratio is used about once now, so it might be a good start. I’ll try Codea’s search function to see what we find:

OK, a few times. It’s set in draw, and applied in two places, once in Universe and once in Asteroid. One of uses is a mistake:

function Universe:moveObject(anObject)
    local pos = anObject.pos + Ratio*anObject.step
    anObject.pos = vec2(self:keepInBounds(pos.x, WIDTH), self:keepInBounds(pos.y, HEIGHT))    
end


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

That second usage is a mistake! On my hot iPad, Ratio is always 1, so we don’t notice the effect. On my slower iPad, I noticed last night that broken asteroids ran twice as fast as the big ones. And I found that bug. And I made a mental note to fix it this morning. You see how well that worked. Anyway we’ve found it again. I’ll remove that one and fix the other references to be inside Universe:

function Universe:init()
    self.ship = createShip()
    self.processorRatio = 1.0
    self.asteroids = {}
    self.missiles = {}
    self.explosions = {}
    self.missileVelocity = vec2(MissileSpeed,0)
end

function Universe:draw()
    self.processorRatio = DeltaTime/0.0083333
    self:drawAsteroids()
    self:drawExplosions()
end


function Universe:moveObject(anObject)
    local pos = anObject.pos + self.processorRatio*anObject.step
    anObject.pos = vec2(self:keepInBounds(pos.x, WIDTH), self:keepInBounds(pos.y, HEIGHT))    
end

I renamed it processorRatio, hoping that was a better name.

Now what else is in setup? Not much.

function setup()
    U = Universe()
    U:createAsteroids()
    Score = 0
    --displayMode(FULLSCREEN_NO_BUTTONS)
    createButtons()
end

Now createButtons is in the Button tab right now, and it won’t care where it’s called from so we can just move that create over to Universe:

function Universe:init()
    createButtons()
    self.ship = createShip()
    self.processorRatio = 1.0
    self.asteroids = {}
    self.missiles = {}
    self.explosions = {}
    self.missileVelocity = vec2(MissileSpeed,0)
end

Everything still works. Commit: “move ratio and createButton to universe”.

I’m curious how the buttons work, that is, how the ship interacts with them. No, I don’t remember: I have a computer for that. Ah, yes, there is a global table, Button, that includes values true / false for left, right, go, and fire. Let’s move that into Universe and access it from there. That will involve a few changes:

function Universe:init()
    self.button = {}
    createButtons()
    self.ship = createShip()
    self.processorRatio = 1.0
    self.asteroids = {}
    self.missiles = {}
    self.explosions = {}
    self.missileVelocity = vec2(MissileSpeed,0)
end


function moveShip()
    if U.button.left then Ship.radians = Ship.radians + rotationStep end
    if U.button.right then Ship.radians = Ship.radians - rotationStep end
    if U.button.fire then if not Ship.holdFire then fireMissile() end end
    if not U.button.fire then Ship.holdFire = false end
    actualShipMove()
end


function checkButtons()
    U.button.left = false -- CHANGED on down
    U.button.right = false
    U.button.go = false
    U.button.fire = false
    for id,touch in pairs(Touches) do
        for i,button in ipairs(Buttons) do
            if touch.pos:dist(vec2(button.x,button.y)) < 50 then
                U.button[button.name]=true -- CHANGED
            end
        end
    end
end

function drawButtons()
    pushStyle()
    ellipseMode(RADIUS)
    textMode(CENTER)
    stroke(255)
    strokeWidth(1)
    for i,b in ipairs(Buttons) do
        pushMatrix()
        pushStyle()
        translate(b.x,b.y)
        if U.button[b.name] then -- CHANGED
            fill(128,0,0)
        else
            fill(128,128,128,128)
        end
        ellipse(0,0, 50)
        fill(255)
        fontSize(30)
        text(b.name,0,0)
        popStyle()
        popMatrix()
    end
    popStyle()
end


function actualShipMove()
    if U.button.go then -- CHANGED
        local accel = vec2(0.015,0):rotate(Ship.radians)
        Ship.step = Ship.step + accel
        Ship.step = maximize(Ship.step, 3)
    end
    finallyMove(Ship)
end

The Codea search helped me find those, even though it’s not the best search ever. It finds too much but that’s better than finding too little.

The game still plays, commit: “buttons in Universe”.

I think there’s something weird about the buttons, but I’m not clear how to improve it just now. They’re somewhat weird by their nature. They could be generalized but I see no reason to do that just now. Similarly, they might be made more object-oriented but why?

We’ll stick with working on moving things from Main to Universe. Here’s Main, except for touched:

function setup()
    U = Universe()
    U:createAsteroids()
    Score = 0
    --displayMode(FULLSCREEN_NO_BUTTONS)
end

function draw()
    --displayMode(FULLSCREEN_NO_BUTTONS)
    checkButtons()
    pushStyle()
    background(40, 40, 50)
    U:draw()
    drawButtons()
    drawShip()
    moveShip()
    U:drawMissiles()
    drawSplats()
    drawScore()
    popStyle()
    U:findCollisions()
end

function drawScore()
    local s= "000000"..tostring(Score)
    s = string.sub(s,-5)
    fontSize(100)
    text(s, 200, HEIGHT-60)
end

We can move checkButtons over, and obviously U:findCollisions belongs over there. Ultimately all of the drawing should move over bit by bit. It’s tempting to move them all in a batch. It’s also a bit risky. But hey, let’s go for it, I’m going to move everything from background on down. No, the heck with it. I’m moving the whole thing, leaving only U:draw. Hold my Diet Coke.

function Universe:draw()
    --displayMode(FULLSCREEN_NO_BUTTONS)
    pushStyle()
    background(40, 40, 50)
    self.processorRatio = DeltaTime/0.0083333
    self:drawAsteroids()
    self:drawExplosions()
    checkButtons()
    drawButtons()
    drawShip()
    moveShip()
    self:drawMissiles()
    drawSplats()
    drawScore()
    popStyle()
    self:findCollisions()
end

I reordered things just enough to move the display setting stuff to the top. The game still runs. Commit: “move all drawing to Universe”.

Now Main looks almost clean:

-- Asteroids
-- RJ 20200511

Touches = {}
Score = 0

function setup()
    U = Universe()
    U:createAsteroids()
    Score = 0
end

function draw()
    U:draw()
end

function drawScore()
    local s= "000000"..tostring(Score)
    s = string.sub(s,-5)
    fontSize(100)
    text(s, 200, HEIGHT-60)
end

function touched(touch)
    if touch.state == ENDED or touch.state == CANCELLED then
        Touches[touch.id] = nil
    else
        Touches[touch.id] = touch
    end
end

We’ll move score:

function Universe:init()
    self.score = 0
    self.missileVelocity = vec2(MissileSpeed,0)
    self.button = {}
    createButtons()
    self.ship = createShip()
    self.processorRatio = 1.0
    self.asteroids = {}
    self.missiles = {}
    self.explosions = {}
end


function drawScore()
    local s= "000000"..tostring(U.score)
    s = string.sub(s,-5)
    fontSize(100)
    text(s, 200, HEIGHT-60)
end

We’ll make drawScore an instance method. (And should do so with some other functions here:

function Universe:drawScore()
    local s= "000000"..tostring(self.score)
    s = string.sub(s,-5)
    fontSize(100)
    text(s, 200, HEIGHT-60)
end

Commit: “move score to universe”.

When we complete this move to object-orientation, probably there will be no plain function calls in Universe:draw() but for now it looks like this:

function Universe:draw()
    --displayMode(FULLSCREEN_NO_BUTTONS)
    pushStyle()
    background(40, 40, 50)
    self.processorRatio = DeltaTime/0.0083333
    self:drawAsteroids()
    self:drawExplosions()
    checkButtons()
    drawButtons()
    drawShip()
    moveShip()
    self:drawMissiles()
    drawSplats()
    U:drawScore()
    popStyle()
    U:findCollisions()
end

It’s 9 PM. Let’s sum up.

Summing Up

We’ve moved all the game specific stuff out of Main, leaving just a generic Touches table. I think I might move that out sometime as well.

Universe is taking on more of its responsibilities to know all the constants and objects. We aren’t all the way to what I’d call good design – and we may never get all the way, since there are features to write and no one gets to perfection anyway.

But in the course of about an hour, the code is better than it was, by my standards. And we can almost always find an hour to keep things clean.

Oh. I forgot to paste the code at the bottom. Too late now, I’ll do it in tomorrow’s article.

See you next time!