A Löve2D Tutorial — Part 01

italo maia
5 min readDec 31, 2018

--

These last few days I’ve been thinking that it would be nice (and fun!) to make a tutorial on something related to the Lua programming language.

As I’ve been trying to make something related to the game development framework Löve2D for a while, this was the perfect chance!

This tutorial is heavely based on the tutorial available at nerd paradise, so, any coincidences are intentional and expected.

So, what do you need to know for this tutorial?

  1. Basic knowledge of the Lua 5.1 Programming Language.
  2. Basic knowledge on Linux Debian/Ubuntu
  3. Editor with Lua Highlight (zerobrane is suggested, but vscode, atom, sublimetext, medit or howl will work)

Setting Up

Install our editor of choice, ZeroBrane, from here. And install love from the command below:

> sudo add-apt-repository ppa:bartbes/love-stable; sudo apt-get update; sudo apt-get install -y love; love --version

The Anatomy of a Löve2D Game

A Love2D game is just a folder with a main.lua file inside, calling the love framework. So, let’s create one ourselves:

> mkdir game
> touch game/main.lua
> tree game
game
└── main.lua
0 directories, 1 file
> echo "function love.draw()
love.graphics.print('Hello World', 400, 300)
end" > game/main.lua

Above we have a full “hello world” game made with Löve2D. Run it with love game

You should see a black screen with the text “Hello World” inside, like below:

Hello World “game”

So far, so good. Not too exciting, but we’ll get there! Anyhow, what did we do exactly to get our hello world working?

Well, we override the function love.draw which is one of the three super special callbacks Löve uses to make things happen. love.draw allows you to “paint” the current frame with whatever you want. It basically handles “drawing”. The other two callbacks are love.update and love.load. Let’s take a look at all three real quick:

-- source: https://love2d.org/wiki/love
-- loads and initializes resources
function love.load()
-- load images, audio, fonts, set variables, etc here
end

-- called on each frame update
function love.update(dt)
-- dt time in seconds (or fraction of second) since
-- the last update
-- handles user events, like key pressed, mouse clicks, etc
end

-- draws images, shapes, text here
function love.draw()
-- in our example, we use love.graphics.print to
-- draw a label for us
end

What about drawing something more exciting, like … a blue rectangle?

Create a new file called conf.lua:

> touch conf.lua

And update the code as follows:

-- file:conf.lua
-- conf.lua is loaded before löve modules
-- use it for configuration
function love.conf(t)
-- 400x300 (WxH)
t.window.width = 400
t.window.height = 300
end
-- file:main.lua
function love.draw()
-- color is set as rgba, where each values is between 0 and 1
love.graphics.setColor(.08, .58, .81)
-- create a "filled" rectangle in position 30x30 with size 60x60
love.graphics.rectangle("fill", 30, 30, 60, 60)
end

Tip: to get a rgb value between 0 and 1, just divide your regular 0..255 rgb value by 255.

Interacting With The Game

Our cool blue rectangle could become much cooler if we could interact with it. How about we make it possible to control the rectangle color using our keypad?

local colors = {.08, .58, .81}  -- rgb values
local color_pick = 1 -- which color are we interacting with
local color_step = 0.1 -- how fast should colors change
local color_ch = 0 -- used to control which way is the change
local key_ch_pr_map = {up = color_step, down = -color_step}
local key_ch_rl_map = {up = true, down = true}
function love.update(dt)
local color = colors[color_pick] + color_ch * dt
colors[color_pick] = math.min(1, math.max(0, color))
end
function love.keyreleased(key)
if key_ch_rl_map[key] then
color_ch = 0 -- reset on key release
end
end
function love.keypressed(key)
color_ch = key_ch_pr_map[key] or 0

if key == "left" then
color_pick = math.max(1, color_pick - 1)
end

if key == "right" then
color_pick = math.min(#colors, color_pick + 1)
end
end
function love.draw()
-- color is set as rgba, where each values is between 0 and 1
love.graphics.setColor(colors[1], colors[2], colors[3])
-- create a "filled" rectangle in position 30x30 with size 60x60
love.graphics.rectangle("fill", 30, 30, 60, 60)

-- text should be white
love.graphics.setColor(1, 1, 1)
-- our fancy, on screen, debug
love.graphics.print("color pick: " .. color_pick, 30, 100)
love.graphics.print("color: " .. colors[color_pick], 30, 120)
end
Try pressing left, right, up and down, in your keyboard

There you go. The code is quite simple. We create two new callbacks, keyreleased and keypressed that handle the events which they are named after and set some variables when the desired keys are pressed.

We multiply color_ch by dt because update is called hundreds of times (at least in my machine) during the time of a single key press. As we don’t want the change to be instantaneous or “too much too fast”, we use dt to “hold” the change, making it more natural.

Notice that pressed keys are named in Löve, instead of numeric codes, as it is usually done by other frameworks. That makes it much easier for beginners and less likely to create bugs. For a list of all available codes, go here.

What If The Rectangle Moved?!!

Things that move are cool, right? What about if our rectangle could move?! To do that, we need to change what happens when we press the arrow keys.

local colors = {.08, .58, .81}  -- rgb values
local pos = {30, 30}
local move_step = 16
local move = {0, 0}
local key_mv_x_rl_map = {
left = true,
right = true,
}
local key_mv_y_rl_map = {
up = true,
down = true,
}
local key_mv_x_map = {
left =-move_step,
right = move_step,
}
local key_mv_y_map = {
up =-move_step,
down = move_step,
}
function love.update(dt)
pos[1] = pos[1] + move[1] * dt
pos[2] = pos[2] + move[2] * dt
end
function love.keyreleased(key)
-- if arrow key is released, reset movement
move[1] = key_mv_x_rl_map[key] and 0 or move[1]
move[2] = key_mv_y_rl_map[key] and 0 or move[2]
end
function love.keypressed(key)
-- if an arrow key is pressed, update move
move[1] = key_mv_x_map[key] and key_mv_x_map[key] or move[1]
move[2] = key_mv_y_map[key] and key_mv_y_map[key] or move[2]
end
function love.draw()
-- color is set as rgba, where each values is between 0 and 1
love.graphics.setColor(colors[1], colors[2], colors[3])
-- create a "filled" rectangle in position "pos" with size 60x60
love.graphics.rectangle("fill", pos[1], pos[2], 60, 60)
end

Movement is a illusion. If what you see changes fast enough, your brain will connect the missing “frames” and make you believe something moved. That is what we do above. While the arrow key is pressed, we move our rectangle, little by little, and stop it on release.

Try messing around with the movement speed, colors, shapes … Take a look at the graphics module, for reference.

--

--