loading...

Dim Jump, Löve and Lua

buntine profile image Andrew Buntine ・3 min read

I've recently been surveying a few 2D game development frameworks, of which there are a literal shittonne. I stumbled upon one called Löve2d that looked promising. A quick browse around the documentation made it clear that it didn't suffer from the same issues Pygame had with it's limited support for sound management. The primary language was Lua, which I was familiar with but had never actually used.

I'd seen a simple game (called Box Jump) in which the player travels across the screen at a constant speed and needs to jump over obstacles. I decided I wanted to make a slightly modified version so that the player also needed to "duck" some of the obstacles. I also decided to up the ante on the difficulty factor!

The finished result is: DIM JUMP

Let me know if you can beat it! My best is 150 deaths.

It's been great fun to create. I've also received some positive feedback, which has motivated me to start planning a far more advanced version of the game. Watch this space...

The Love2D framework certainly gets a "thumbs up" from me. It's general pattern of splitting flow into load. draw, update and quit was nice to work with. One thing that did bother me was the "globality" of graphical changes. For example, in order to draw some red text at 16px followed by some green text at 12px, one needs to:

local oldR, oldG, oldB = love.graphics.getColor()
local oldFont = love.graphics.getFont()
local smallFont = love.graphics.newFont("Arial", 12)
local largeFont = love.graphics.newFont("Arial", 16)

love.graphics.setColor(255, 0, 0) -- red, green, blue
love.graphics.setFont(smallFont)

love.graphics.print("Hello", 10, 10) -- string, x, y

love.graphics.setColor(0, 255, 0)
love.graphics.setFont(largeFont)

love.graphics.print("World", 100, 10)

love.graphics.setColor(oldR, oldG, oldB)
love.graphics.setFont(oldFont)

Needless to say, this becomes tedious. Lua does have support for first-class functions and so abstracting the mess is relatively easy:

function withColour(r, g, b, a, f)
  local _r, _g, _b, _a = love.graphics.getColor()

  love.graphics.setColor(r, g, b, a)
  f()
  love.graphics.setColor(_r, _g, _b, _a)
end

function withFont(name, f)
  local _f = love.graphics.getFont()

  love.graphics.setFont(fonts[name])
  f()
  love.graphics.setFont(_f)
end

So now we can rewrite the program as:

local smallFont = love.graphics.newFont("Arial", 12)
local largeFont = love.graphics.newFont("Arial", 16)

withColor(255, 0, 0, 0, function ()
  withFont(largeFont, function ()
    love.graphics.print("Hello", 10, 10)
  end
end)

withColor(0, 255, 0, 0, function ()
  withFont(smallFont, function ()
    love.graphics.print("World", 100, 10)
  end
end)

Which brings me to Lua; you don't get a lot out of the box. It was designed primarily to be embedded into much larger programs. Picking it up wasn't too much of a challenge as it's prototypal OO model, first-class functions and full lexical scoping are similar to those of Javascript.

One thing that really exlemplifies Lua's design simplicity is the fact that apart from the obvious atomic data structures (booleans, numbers, etc), Lua supports only the associative array (or "table" in Lua lingo). Arrays, sets and other collections are defined in terms of tables. So, in effect, an array is really just a table of:

{[1] = "a", [2] = "b", [3] = "c"}

-- Lua also provides syntactic sugar for this:
{"a", "b", "c"}

Lua is also very "forgiving" and has some odd semantics (again like JS). None of the following will cause an exception:

coolness = {bunts=100, erik=101}

coolness.brendan -- nil
"45" + 1 -- 46
print(madeupname) -- nil

a,b,c = 1,2
print(a) -- 1
print(b) -- 2
print(c) -- nil

Make of that what you will! Happy coding...

Posted on by:

buntine profile

Andrew Buntine

@buntine

Conjurer of computational spells and trickery.

Discussion

markdown guide
 

Nice use of the decorator pattern with withColour and withFont. I think I'll take those into use myself. :)