In the previous post, we talked about how Unity developers often bring the multiple MonoBehaviour mindset into Defold, and how that can lead to overusing scripts and message passing.
In this post, we’ll look at another very common Unity pattern—especially in simple or one-scene games:
The GameManager Singleton
And how that idea translates naturally into Defold.
If you’ve built small games in Unity (arcade, hypercasual, jam games), you’ve probably done something like this:
public class GameManager : MonoBehaviour
{
public static GameManager Instance;
public int score;
public GameObject gameOverPanel;
public Text scoreText;
public Player player;
void Awake()
{
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
}
void OnDestroy()
{
if (Instance == this)
{
Instance = null;
}
}
}
Sometimes it grows into:
GameManagerScoreManagerUIManagerAudioManagerAll accessible globally.
In large-scale software, singletons are often discouraged.
But in small games?
They’re everywhere.
Why?
So in practice:
For small games, singletons are often a pragmatic choice
Defold doesn’t have MonoBehaviour, and you don’t attach scripts everywhere the same way.
But here’s the important part:
The pattern itself still works
In Lua, modules are naturally shared because of require() caching.
That means:
A Lua module can act like a singleton.
Here’s a basic version:
local gameplay_state = {}
function gameplay_state.init()
gameplay_state.score = 0
gameplay_state.current_hp = 3
print("gameplay_state init")
end
function gameplay_state.cleanup()
print("gameplay_state cleanup")
end
function gameplay_state.decrease_hp()
gameplay_state.current_hp = gameplay_state.current_hp - 1
end
function gameplay_state.add_score(amount)
gameplay_state.score = gameplay_state.score + amount
end
return gameplay_state
Usage:
--- in your gameplay.script
local gameplay_state = require "modules.gameplay_state"
function init(self)
gameplay_state.init()
end
This already behaves very similarly to a Unity singleton.
In Unity:
In Defold:
So you must explicitly reset:
--- in your gameplay.script
function init(self)
--- call it once
gameplay_state.init()
end
And optionally clean up:
--- in your gameplay.script
function final(self)
gameplay_state.cleanup()
end
A common mistake is exposing variables directly:
gameplay_state.score = gameplay_state.score + 10 -- ❌ risky
A better approach:
local gameplay_state = {}
local state = {
score = 0,
current_hp = 3,
}
function gameplay_state.init()
state.score = 0
state.current_hp = 3
end
function gameplay_state.get_score()
return state.score
end
function gameplay_state.add_score(value)
state.score = state.score + value
end
function gameplay_state.decrease_hp()
state.current_hp = state.current_hp - 1
end
function gameplay_state.is_gameover()
return state.current_hp <= 0
end
return gameplay_state
Using modules like this gives you:
It fits especially well for:
Just like in Unity, this pattern can go too far.
Be careful not to turn your module into:
A hidden “God Object”
Avoid stuffing everything into one module:
-- ❌ Don't do this
gameplay_state.player
gameplay_state.enemies
gameplay_state.ui_nodes
Keep it focused on global state, not object behavior.
The singleton pattern itself exists in both Unity and Defold.
The difference is mostly in how developers commonly implement it.
In Unity, singleton-style systems are often built around:
MonoBehaviourInstanceIn Defold, the equivalent approach is often much simpler:
No GameObject is required unless you actually need one.
This makes Defold’s approach feel more lightweight and explicit, especially for small games.
The “GameManager Singleton” pattern doesn’t disappear when you move from Unity to Defold.
It simply becomes:
A Lua module with controlled state and functions
Singleton-style architecture is one of those patterns that is very easy to misuse, but also very easy to understand and work with, especially for small games.
That’s why despite all the warnings around singletons, many developers still use them.
In Defold, Lua modules make this approach feel very natural and lightweight.
The important part is not whether you use a singleton-like pattern or not, but:
Understanding the scope and complexity of your game
A simple architecture that fits your project is often better than an overly “correct” architecture that slows development down.
In upcoming posts, we’ll explore other ways to structure game logic in Defold. Thanks for reading!