Recently, I decided to spend a bit of time exploring LÖVE in my spare time. It uses Lua as programming language. Also, it’s an open-source engine available for free on Windows, Mac, and Linux. You can also build your game for iOS/Android and even WebGL.
Website: https://love2d.org/
I thought it would be easy, coming from Unity and different other languages. But it took me a bit of time to find the information or logic behind the language and engine.
So I decided to write this guide to help you get kick-started and easily transitioned to LÖVE.
Here are some examples of different things you may want to use:
local x = 1 -- Only available in local scope
globalVar = 2 -- Global variable
-- increment variable
i = i + 1
-- decrement variable
i = i - 1
-- concatenate the variable name with two strings
message = "Hello, my name is "..name.."!"
-- not equal operator (others are the same as others languages)
test = something ~= 0
-- logical operatiors: not, and, or
test1 = not something
test2 = something and somethingelse
test3 = something or somethingelse
-- tertiary operator (a ? b : c)
test = a and b or c
Note: there is no i++
in Lua to increment.
if playerJumped then
-- Start jump animation
elseif playerCrouch then
-- Start crouch animation
else
-- Play idle animation
end
-- Loop over i from 0 to 20 and increment by 1 at each cycle
for i = 0,20,1
do
-- Do something
end
Note: you cannot have multiple variables in the for statement. If you need multiple variables look at doing a while
loop instead.
local i = 0
while i < 20
do
-- Do something
i = i + 1
end
function f(argument1)
return something * argument1
end
Note: all the primary types (numbers, boolean, nil, strings) are passed by values, everything else is passed by reference to the function.
There is a native way of doing it, using the official language reference: https://www.lua.org/pil/16.1.html
But I found it a bit inelegantly and the function setmetadatatable
is confusing.
So I found another solution that simplifies the code you need to write to create classes and child classes: https://gist.github.com/paulmoore/1429475
You only need to import the file classes.lua
in your project, but definitely look at the examples.
But here is a quick example.
-- Load the classes helper file
local classes = require "classes"
local Enemy = classes.class() -- Create a class without any parent
Enemy.count = 0
function Enemy:init(name)
self.hp = 100
self.name = name
Enemy.count = Enemy.count + 1
end
-- static function
function Enemy.count()
return Enemy.count
end
local Orc = classes.class(Enemy) -- Create an Orc who is an enemy
function Orc:init(name, weapon)
self.super:init(name) -- call to parent constructor
self.weapon = weapon
end
-- Instantiate some orcs
local orcWithAxe = Orc.new("Zotor", "Axe")
local orcWithBow = Orc.new("Plead", "Bow")
Notes:
new
to instantiate and define init
in the class, which is called implicitly during the new
operation..
then the name of the element you want to access.:
then the name of the element you want to access.For that, I made a module based on the example provided by the official language reference. https://www.lua.org/pil/11.4.html
List = {}
function List.new ()
return {first = 0, last = -1}
end
function List.count(l)
return #table
end
function List.pushleft (list, value)
local first = list.first - 1
list.first = first
list[first] = value
end
function List.pushright (list, value)
local last = list.last + 1
list.last = last
list[last] = value
end
function List.popleft (list)
local first = list.first
if first > list.last then error("list is empty") end
local value = list[first]
list[first] = nil -- to allow garbage collection
list.first = first + 1
return value
end
function List.popright (list)
local last = list.last
if list.first > last then error("list is empty") end
local value = list[last]
list[last] = nil -- to allow garbage collection
list.last = last - 1
return value
end
return List
Copy that in a file list.lua
in your project. Then you can use it this way:
queue = List.new()
stack = List.new()
-- Enqueue values
List.pushright(queue, someValue)
List.pushright(queue, someValue)
List.pushright(queue, someValue)
-- Stack values
List.pushleft(stack, someValue)
List.pushleft(stack, someValue)
List.pushleft(stack, someValue)
-- Consume queue (FIFO)
local value1 = List.popleft(queue)
local value2 = List.popleft(queue)
-- Consume stack (LIFO)
local value3 = List.popleft(stack)
local value4 = List.popleft(stack)
Table is a specific type in Lua, it’s a bit like arrays in PHP, which can be associative or indexed.
-- same initialization
local array = {}
local table = {}
-- initializing array values
for i = 0,20,1
do
array[i] = i
end
-- initialization table values
table["x"] = 0
table["y"] = 0
-- or could be done at the declaration
local coords = {x=0, y=0}
If you want to schedule a specific action at a certain time, you want to use cron/timer. The easiest solution I found is to use the tool made by kikito: https://github.com/kikito/cron.lua
local cron = require 'cron'
local function printMessage()
print('Hello')
end
-- the following calls are equivalent:
local c1 = cron.after(5, printMessage)
local c2 = cron.after(5, print, 'Hello')
c1:update(2) -- will print nothing, the action is not done yet
c1:update(5) -- will print 'Hello' once
c1:reset() -- reset the counter to 0
-- prints 'hey' 5 times and then prints 'hello'
while not c1:update(1) do
print('hey')
end
-- Create a periodical clock:
local c3 = cron.every(10, printMessage)
c3:update(5) -- nothing (total time: 5)
c3:update(4) -- nothing (total time: 9)
c3:update(12) -- prints 'Hello' twice (total time is now 21)
OK, Lua itself requires some learning, as you’ve seen. But LÖVE too!
To run a game, you need a folder with at least a file main.lua
.
Example of minimal structure:
function love.load()
-- Run once at game initialization
end
function love.keypressed(key, scancode, isrepeat)
-- Run each time a key on the keyboard is pressed
end
function love.mousepressed(x, y, button, istouch, presses)
-- Run each time a mouse button is pressed, supports multi-touch too
end
function love.update(dt)
-- Run at each frame before drawing it
-- This is where you handle most of your game logic
end
function love.draw()
-- Called at every frame to draw the game
end
Note: There are other functions you may need, you can find the details here: https://love2d.org/wiki/love
I advise you to use Visual Code with the extension https://marketplace.visualstudio.com/items?itemName=SK83RJOSH.love-launcher
Also, add a file conf.lua
at the root of your folder, with the contents:
function love.conf(t)
t.console = true
end
Now each time you press Alt+L
, your game will pop plus the console window which can help to debug.
You can change the default window display attribute during the loading of your game. Example:
function love.load()
love.window.setMode(800, 600, {resizable=true, minwidth=400, minheight=300})
end
Details: https://love2d.org/wiki/love.window.setMode
I found a nice package that adds support for SVG files to LÖVE. It is called TÖVE.
Details: https://github.com/poke1024/tove2d
No active development since a couple of years ago, so be aware of that. But for me, it worked fine.
If you want to make your game at a constant frame rate, it’s not possible natively.
But I found that gist from Leandros: https://gist.github.com/Leandros/98624b9b9d9d26df18c4 that just does the job perfectly.
You need to copy/paste the code at the end of your main.lua and that’s it. Constant 60FPS.
You can tweak the FPS if you need.
LÖVE is not natively using the paradigm ECS. But if you like ECS as I do, I found a tiny ECS framework for it. It was written in 2016, pretty old already, but it’s so tiny that it did age well.
Here is the link: https://github.com/bakpakin/tiny-ecs
If you want to use it, there’s only one file tiny.lua
to copy to your project.
You can find a project using it here as an example: https://github.com/bakpakin/CommandoKibbles
If you want to have some post-processing screen virtual effects, the easiest I found is to use moonshine: https://github.com/vrld/moonshine
It contains 16 effects like glow, vignette, crt, ... That you can chain as you want and change parameters on the fly.
If you want to create your own effect, you will have to start to write shaders and extend moonshine effects.
Check the GitHub README to understand how to use it.
I found on the forum of LÖVE a reply from vrld that does the job: https://love2d.org/forums/viewtopic.php?p=193765#p193765
local t, shakeDuration, shakeMagnitude = 0, -1, 0
function startShake(duration, magnitude)
t, shakeDuration, shakeMagnitude = 0, duration or 1, magnitude or 5
end
function love.update(dt)
if t < shakeDuration then
t = t + dt
end
end
function love.draw()
if t < shakeDuration then
local dx = love.math.random(-shakeMagnitude, shakeMagnitude)
local dy = love.math.random(-shakeMagnitude, shakeMagnitude)
love.graphics.translate(dx, dy)
end
end
If you want to distribute your game, your starting point is the wiki at that address: love2d.org/wiki/Game_Distribution
First, you need to install the tool from Davidobot: https://github.com/Davidobot/love.js
npm install -g love.js
Web server with proper headers
Then you will need a server and a web server running on it. On my end, I use Nginx, and this is how I configured my location
:
location /beathoven/ {
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
alias /usr/share/nginx/html/beathoven/;
}
Add missing mime type
After several tries looking at errors in the browser, I realize also I needed to edit the file /etc/nginx/mime.types
to add the definition for wasm file:
application/vnd.google-earth.kmz kmz;
application/wasm wasm;
application/x-7z-compressed 7z;
Use 7zip not tar on Windows
To package your game into a love file, you need to simply zip the files.
And since Windows 10, Windows includes a tar
command line utility that you can use to make zip. But it looks like it’s not supported by the WebGL build. So I switched to 7Zip using the following parameters:
You can also use 7zip in the command line.
Example of building & deploy script
#!/bin/bash
tools/7za a -tzip Beathoven.zip assets/ gameobjects/ pages/ responsive/ utils/ fonts.lua main.lua -mx0
mv Beathoven.zip Beathoven.love
love.js -t Beathoven -c Beathoven.love game
ssh server rm -rf /usr/share/nginx/html/beathoven/*
scp -r game/* server:/usr/share/nginx/html/beathoven/
Obviously, I haven’t covered everything here, but I hope that it can give a nice introduction to help you get quickly productive with LÖVE and create some cool games!
Next week I’ll share about a Rhythm game called Beathoven I decided to make while exploring the possibilities of LÖVE.
Originally published here.