This is Bananattack.

Tag: anonymous functions

Anonymous Functions, and Textshmup: A retrospect

Posted by Overkill on April 8, 2009 at 8:53 pm under Uncategorized

Some of you may have recently played my 24h game textshmup. As expected, it wasn’t that well received. It was a bad concept to begin with, after all! :D

It was too hard to read quickly and type responses. Audio cues and colored text were added to make the game slightly easier to react to, but even then, it was just too tedious. It was just a randomly generated endurance test, with no goal, and no intermediate tasks.

That said, I had fun making it! I mentioned last time that I made it in LuaVerge. I never mentioned why it was so quick to write.

Well let’s go!

For one, I used the vx library I had written. It came with the spiffy nifty object-oriented class system that I made.

I had my entire game use a single well-divided render/update loop, which boiled down to this:

while true do
    Render()
    Update()
end

I declare two lists which contain all the callbacks done by the render and update events:

render_list = {}
update_list = {}

Here is Render(), in its entirety:

function Render()
    vx.screen:RectFill(0, 0, vx.screen.width, vx.screen.height, 0)
    for i, f in ipairs(render_list) do
        f()
    end
    vx.ShowPage()
end

The Render() function starts by clearing the screen, then it iterates over all entries in the render list, and calls them all. At the end, it shows the changes on the screen. This was ridiculously nice to hook in new rendering events.

Similarly, here was my Update() code, which used frame-throttling so each update callback could be written in per-tick logic:

function Update()
    frame_limiter:Update()
    
    local i = 0
    while i < frame_limiter.gap do
        for _, f in ipairs(update_list) do
            f()
        end
        i = i + 1
    end
    vx.SetAppName(TITLE .. ' ' .. frame_limiter.frame_rate)
end

Both of these worked great for global functions and static methods.

There was one issue with the way they were designed though. Which wasn't immediately apparent, but here goes.

Say I made a class named BlueBox, which has an x, y, x2 and y2. I declare a method for it like this.

function BlueBox:Draw()
    vx.screen:RectFill(self.x, self.y, self.x2, self.y2, vx.RGB(0, 0, 255))
end

In Lua, the colon : in that function is syntax sugar for the following:

function BlueBox.Draw(self)
    vx.screen:RectFill(self.x, self.y, self.x2, self.y2, vx.RGB(0, 0, 255))
end

Which is syntax sugar for:

BlueBox.Draw = function(self)
    vx.screen:RectFill(self.x, self.y, self.x2, self.y2, vx.RGB(0, 0, 255))
end

See that 'self' parameter? Well, earlier when I'm calling the functions, I go like this:

f()

That's a call with no arguments. If we try and add the method pointer for a particular blue box's render, it will call it without passing self, so 'self' is nil:

-- Won't pass the self parameter!
table.insert(render_list, self.Render)
-- Similarly, won't pass the self parameter!
table.insert(render_list, self:Render)

Thus object instances aren't passed around. Not good!

So how do you pass around the 'self' parameter to this list? Well, fortunately, Lua allows you to create anonymous functions, wherever you want because functions are first-class values. And these anonymous functions can be passed "upvalues", which are local values defined in a scope outside the function.

Using these facts, I wrote a trivial fix to bind method pointers to their objects, and leave Render unchanged! I made a function that did the method wrapping for me:

function MethodPointer(self, meth)
    return function(...)
        return self[meth](self, ...)
    end
end

The function is passed the 'self' parameter and the method pointer to bind together, and it returns an anonymous function which will take care of calling, with 'self' being passed automatically.

Then I can go add to the render_list and update list:

table.insert(render_list, MethodPointer(self, 'Render'))
table.insert(update_list, MethodPointer(self, 'Update'))

This is actually pretty close to how Javascript gets around similar problems, but with its definition of 'this'.

This little tidbit allowed my various components of my game to plugged and unplugged from the main loop as it progressed.

For my text input, I used a callback to process commands when enter was pressed, using the same method pointer stuff. The callback passed around the piece of text, and would return true or false on whether or not the command was permissible. It was very nice.

Lua is just awesome. I love it.

Anyways. As you might see, coding isn't really a colossal obstacle, with Lua around. Instead, the new obstacle is coming up with good ideas!

Something to remember for the next time I try and make a 24h game, and what I always need to consider, while making Resonance.

Catch you later, everyone!

Tags: , , , , , , ,

No comments (make one!)