I have a few AFK days, so I created a mobile game for my son. It is only a prototype now, but it is already playable.
When I can, I always start what I call a power-of-2 project. In these projects, I do take 16 hours to:
- Think at: what to do?
- I want to learn something new that will help me do that.
- Create something with what I have learned.
If I like it, I will allocate another 16 hours, then another 32, up to 64, and so on, as a power of 2.
What to do?
Like I wrote, I wanted to do a game. But what game? I have multiple games on my iPhone. I tested from one step away what he liked. He is four years old. I saw him choosing fast-action and simple games, like “Basic Hiking,” “Time to React!”, “Geometry,” and similar.
I thought about creating a game with a simple physics world and a set of squares you can tap and throw away. Once you clean up the screen, you move to the next level. It is simple and quick.
The name? Tap and Catch!
Learn something new
Sixteen hours is too short to create a new game engine, so I used a preexisting one. I already know something about Swift, Obj-C, C, etc., so I searched for a game engine that is:
- Small
- It has a physics engine
- Is programmable in Lua
Multiple cross-platform engines allow you to use Lua, but I decided on LÖVE (AKA Love2D) ❤️ because it has everything I need.
I installed the tools I needed.
- LÖVE, of course
- Lua for VSCode https://marketplace.visualstudio.com/items?itemName=sumneko.lua
- Local Lua Debugger also for VSCode https://marketplace.visualstudio.com/items?itemName=tomblind.local-lua-debugger-vscode
I needed to create a couple of files for running and debugging.
.vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "lua-local",
"request": "launch",
"name": "Release",
"program": {
"command": "love"
},
"args": [
".",
],
},
]
}
.vscode/settings.json
{
"Lua.workspace.library": [
"${3rd}/love2d/library",
"lib"
],
"Lua.runtime.version": "LuaJIT",
"Lua.workspace.checkThirdParty": false,
}
The prototype
In the prototype, I just created a Box2D world with the following:
love.physics.setMeter(30)
World = love.physics.newWorld(0, 9.81 * 30, true)
The “30” number means “one meter in the Box2D world is 30 pixels in the monitor”. See official documentation on why 30 is a good one.
“9.81” is the earth’s gravity.
I created a single rectangle attached to the background at the center of the screen with a weld joint in the middle. When the rectangle is clicked, the weld is removed, and a new one is created where the user has clicked.
It is working on macOS. It’s time to move to iOS.
Load the project on iOS
LÖVE does not work well on the iOS simulator, and I didn’t investigate why because I was short on time.

You can technically enable developer mode on your device, set yourself as a trusted developer, and load the app. But I paid Apple for a developer account, and I had an old one from 2007. Deploying is simple:
- Get the iOS source
- Compress all your Lua files
zip -9 -r TAC.love *.lua - Add the
.lovefile to the “Copy Bundle Resources” build phase of thelove-iostarget - Run it
But the red rectangle wasn’t centered anymore on the screen.
The aspect ratio
LÖVE default window is 800×600 pixels. See https://iosref.com/res. But modern phones have a 9:19.5 aspect ratio.
I added this aspect ratio for desktop and mobile to ensure enough room to work on. In the meantime, also set “high DPI” true for retina displays.
function love.conf(t)
local aspectRatioW = 19.5
local aspectRatioH = 9
local width = 932
t.window.width = width
t.window.height = math.floor((width * aspectRatioH) / aspectRatioW)
t.window.highdpi = true
end
Relative positioning is then calculated from the center of the screen.
local w, h = love.graphics.getDimensions()
-- Center is w / 2, h / 2
The following image shows a 24×12 grid with four blue rectangles of the last four iPhone models.

I then choose a height/12 as the base dimension. Everything on the screen will be a multiple of this base, positioning, dimensions, everything. I will not have problems with larger devices and others.
I manually created six rectangles to see if it was working. I changed the weld joint to a mouse joint so you can grab and throw them.
Add a simple menu
I was at ~8 hours and decided to polish things before moving on. I added a simple menu. I drew two lines to ensure that everything was aligned in the center. I had a first misstep.

Quick recalculation:

iOS default fonts are boring, so I decided to use FFF Forward.

FFF Forward is a variable-width font. So, the “S” and the “p” are not aligned. I used love.graphics.print with Font:getWidth to fill the background. It should fill all the background, but a font has:
- A baseline
- A descent
- An height
- A line height
It’s fascinating, but there is not enough time to create a pixel-perfect background. So I decided it was good this way.
The editor
I needed an editor now to create the levels, as I couldn’t do it manually. Time was running short, so I made a quick JS one. Nothing is interesting here, so a video is enough. The editor saves the results as a JSON.
I created a Javascript to Lua tables library to transform the JSON file into a levels.lua file. You can find it here: https://github.com/zaerl/js-to-lua.
From:
{
"name": "Level 1",
"rects": [
{
"x": 24,
"y": 12,
"r": 0
}
]
}
To:
{
name = "Level 1",
rects = { {
x = 24,
y = 12,
r = 0
} }
}
Time has finished
The 16 hours have passed. I created a small working prototype with four levels, and my son is playing it. I think it is promising, so I will extend it, polish the product, and publish it on the App Store for free.
See you at Part 2!