Recently, I found an urge to make a game. I’m not going to talk about the story/world building stuff here for now, just some of the technical aspects.
I have zero experience writing any type of game software, so I expect this to be a project that takes forever. It is entirely possible that it never actually becomes a reality, but for the time being I am actually having fun learning the basics.
I am interested in building the mechanics of the game from scratch, but am not interested in attempting to build an engine on my own. I know I need to start small and use an engine that works for what I’m going for, but doesn’t require a team to get started.
Finding LOVE
There were a lot of options, but ultimately I decided to build with LOVE. It checks a lot of boxes for me:
- open-source
- works on Linux and Windows
- has successful games built, isn’t a “toy”
- has decent documentation
- scriptable - uses Lua for everything
- allows for FFI
I’m a big fan of Lua and use it for lots of small things, so it being the language to write in made me quite happy. As LOVE supports FFI, I can drop in to other libraries as needed.
The Boring Parts
I mainly write software that runs on servers and have only written desktop GUIs in a very limited fashion. I’ve written a handful of JS, HTML and CSS, but the browser handles all of that drawing/painting/etc for you.
Now I have to figure all of that out on my own.
While I would love (no pun intended) to dig in to this here, I want to share some of the boring stuff I’ve been working on: packaging and testing.
Packaging Assets
LOVE has documentation on distribution - ie how to package your game up
using .love
files (zip) and subsequently how to include the engine along with
it. This allows you to package up the code as well as assets in one go.
I imagine that updates to a small game are perfectly fine to be distributed in this fashion, but I wanted to try something a bit different as far as the assets are concerned: custom packaging.
What I came up with was fairly simple: tiny packed files.
struct Package {
u16 PatchID
u32 Count
Entity [Count]Entities
}
struct Entity {
u32 Hash
u32 Size
byte [Size]Content
}
I wrote a simple packer in Go that takes a directory and handles asset packaging from there.
On the LOVE side, I implemented a package set, loader, and entity types.
Package:
- open(): retrieve patch ID
- unpack(): unpack entities, only reading the hash and leaving an offset to where the file is stored on disk
- find(file): find a file by name - hashes the filename and matches entities against it; loads the file based on the offset
Set:
- load(base path, set name): scans base path for packages with the set name prefix, loading all of their patch IDs. Sorts by patch ID desc.
- find(file): calls package:find(file) on packages in desc order to only find the newest version of a given file.
The hope here is that subsequent updates to assets or code contained within
the packages can be dropped in by just downloading a new package file, rather
than an entire .love
and executable.
100% overkill for a game that doesn’t even have an alpha build, but it’s a pet project :)
Testing
As previously mentioned, I have zero experiencing building games. I do, however, have experience writing software in general - and I hate manually testing things that could instead be automated.
As I write code and explore the LOVE APIs, I have written a fair amount of tests on the Lua side of things. For example, the set/package/entity idea above each have their own tests.
Unfortunately, I don’t quite have my local Lua install and LOVE’s version in
sync. Lua, for example, doesn’t have a way to scan a directory for files
without something like luafs
, and I have not been successful in figuring out
how to include these kinds of packages with LOVE itself. I may learn how to do
that in the future, but I didn’t want to focus on it too much and would rather
build the game. I worked around this by using popen
and find
on *nix
and dir
on Windows.
luaunit is a pure-Lua, easy to use test setup that aligns with what I’m used to, so I plugged that in and wrote tests for the non-LOVE reliant code. The problem was that anything using the LOVE APIs directly couldn’t be tested in an automated fashion.
I decided to hack around this by running tests inside of LOVE itself.
Inside of main.lua
, I came up with:
function love.load()
...
if os.getenv("_RUN_LOVE_TESTS") == "Y" then
local suite = require('tests.tests')
local pprint = require('thirdparty.pprint')
-- handle results from suite run, writing suite.output to a file,
-- and showing a message box that indicates success or failure
os.exit( ... )
end
end
tests/tests.lua
is a bit ugly, but it does the trick:
local lu = require('thirdparty.luaunit')
local is_in_love = os.getenv("_RUN_LOVE_TESTS") == "Y"
local love_only = {
['test_pkg'] = true,
['test_set'] = true,
...
}
... file discovery code using popen for running tests outside of love
if not is_in_love then
for i, v in ipairs(files) do
if love_only[v] then
print('skipping ' .. v .. ': requires LOVE')
files[i] = nil
end
end
end
for i = 1, #files do
local file = string.sub(files[i], 1, #files[i] - 4)
if file ~= "tests" then
require('tests.' .. file)
end
end
if is_in_love then
local suite = lu.LuaUnit.new()
suite:runSuite()
return suite
else
os.exit(lu.LuaUnit.run())
end
With all of that set up, I can run both lua tests/tests.lua -v
from the
terminal as well as run _RUN_LOVE_TESTS=Y love src
. The non-pure-lua tests
will be executed within an actual instance of LOVE, allowing full access to any
APIs needed, and a log will be written so I can inspect failures.
End.jpeg
That’s it for now. I expect to write about LOVE again as I work on this, but don’t have any particular timeline envisioned. I’m still learning the ropes in LOVE itself - as well as building sprites, maps, etc.
Hopefully there will be more to share in the future!