Globals and singleton

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Froyok
Prole
Posts: 23
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Globals and singleton

I'm prepping up the global structure of my game and I was wondering what would be the most optimal approach given the layout of code I would like to use. I have read a bit about how Lua handles Globals and they way require works, but I'm wouldn't mind some additional feedback. I feel like my approach could be better but not sure how yet. (Some background: I have experience in Python/C++ but not much with Lua).

To give an example, here is how my structure roughly looks like in main.lua:

Code: Select all

NFS 	= require( "lib.nativefs" )
PATH 	= require( "lib.path.path" )
JSON 	= require( "lib.dkjson.dkjson" )
STRICT 	= require( "lib.luastrict.strict" )

require( "scripts.utils" )
require( "scripts.font" )
require( "scripts.music" )
require( "scripts.input" )
require( "scripts.splash" )
require( "scripts.gamestate" )

FONT:Init()
MUSIC:Init()
SPLASH:Init()
end

function love.update( DeltaTime )
INPUT:Update( DeltaTime )
MUSIC:Update( DeltaTime )
GAME_STATE:Update( DeltaTime )
end

function love.draw()
GAME_STATE:Draw()
end
Example of my script gamestate.lua:

Code: Select all

GAME_STATE = {
StateList = {
"Splash",
"Map",
"World",
"Fight",
"Credits"
},

CurrentState = "Splash",

----------------------------------------
GetCurrentState = function (self)
return self.CurrentState
end,

----------------------------------------
Update = function(self, DeltaTime)
if self.CurrentState == "Splash" then
SPLASH:Update( DeltaTime )
end
end,

----------------------------------------
Draw = function(self)
if self.CurrentState == "Splash" then
SPLASH:Draw()
end
end
}
Other "modules" are pretty much using the same logic: a global variable stores an object that serves as a singleton.
This is convenient for me to be able to reference each object via other script, kind of like a system.

So now I know I could store the reference to the global into a local variable inside each script to speed things up a bit, however I wonder if there is an even better approach ? I prefer to look into that now before having to refactor a lot of code later.
pgimeno
Party member
Posts: 3232
Joined: Sun Oct 18, 2015 2:58 pm

Re: Globals and singleton

You can do away with the globals altogether by just assigning the require to a local. Using locals instead of globals has one fundamental advantage: they make files much more readable in that it is self-contained. If you forget where a certain variable was defined or modified, you don't have to search for the file in which it was defined or modified: the require() line will tell you where it is defined, and the parameter passing will tell you where it is modified.

Apart from that, they are faster to access, albeit probably marginally, due to not needing a table lookup.
Froyok
Prole
Posts: 23
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Re: Globals and singleton

Noted on the require. Makes sense after reading how that function works.
pgimeno wrote: Mon Dec 06, 2021 1:26 pmthe require() line will tell you where it is defined, and the parameter passing will tell you where it is modified.
I'm not sure what you mean by parameter passing ? You mean to the function from the global in case I have a callstack to follow ?
pgimeno
Party member
Posts: 3232
Joined: Sun Oct 18, 2015 2:58 pm

Re: Globals and singleton

Froyok wrote: Mon Dec 06, 2021 1:42 pm I'm not sure what you mean by parameter passing ? You mean to the function from the global ?
If the variables stop being global, in order to do something with them in other places, you need to pass them as parameters. That's what I was referring to.
Froyok
Prole
Posts: 23
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Re: Globals and singleton

pgimeno wrote: Mon Dec 06, 2021 1:46 pm If the variables stop being global, in order to do something with them in other places, you need to pass them as parameters. That's what I was referring to.
Can't I just return my object like this and use the requires ? Taking advantage of the caching.

Simplified example:
main.lua

Code: Select all

local SPLASH = require( "scripts.splash" )
local GAME_STATE = require( "scripts.gamestate" )

SPLASH:Init()
end

function love.update( DeltaTime )
GAME_STATE:Update( DeltaTime )
end

splash.lua

Code: Select all

local SPLASH = {
WindowWidth = 720,
WindowHeight = 340
}

return SPLASH
gamestate.lua

Code: Select all

local SPLASH = require( "scripts.splash" )

GAME_STATE = {
Update = function(self, DeltaTime)
if self.CurrentState == "Splash" then
SPLASH:Update( DeltaTime )
end
end
}

return GAME_STATE
Seems to be working, but I'm not familiar enough to foresee the consequences of this pattern.

Otherwise I think I understand now what you mean by passing as parameters. Which I could do via the Init() function and store a reference this way I guess, but that would make them tedious to maintain (I did that once in another project).
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Globals and singleton

Froyok wrote: Mon Dec 06, 2021 5:35 pm

Code: Select all

GAME_STATE = {
Update = function(self, DeltaTime)
if self.CurrentState == "Splash" then
SPLASH:Update( DeltaTime )
end
end
}

If you made self.CurrentState the actual state instead of a string with the state's name, you could do away with the ifs.

You could also return a fully initialized SPLASH table in splash.lua and delete that love.load nonsense. By the time main.lua runs and you require the file, everything you need from love is ready to use.
Froyok
Prole
Posts: 23
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Re: Globals and singleton

grump wrote: Mon Dec 06, 2021 5:50 pm If you made self.CurrentState the actual state instead of a string with the state's name, you could do away with the ifs.
That's a good point. I just kept my code snippet simple to simplify the discussion around. I have other stuff planned in the state update function.
grump wrote: Mon Dec 06, 2021 5:50 pm You could also return a fully initialized SPLASH table in splash.lua and delete that love.load nonsense. By the time main.lua runs and you require the file, everything you need from love is ready to use.
I thought love.load() was the perfect place to do this kind of stuff before any rendering/update, so why going away from it ? I fail to see how it's "nonsense".
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Globals and singleton

Froyok wrote: Mon Dec 06, 2021 6:37 pm I thought love.load() was the perfect place to do this kind of stuff before any rendering/update, so why going away from it ? I fail to see how it's "nonsense".
All it does it add complexity. The explicit :init call is completely unnecessary.

love.load is a remnant from darker times and it doesn't have a purpose anymore. You can use it if writing more code for no real reason makes you happy.
pgimeno
Party member
Posts: 3232
Joined: Sun Oct 18, 2015 2:58 pm

Re: Globals and singleton

Froyok wrote: Mon Dec 06, 2021 5:35 pm Can't I just return my object like this and use the requires ? Taking advantage of the caching.
Yes, except...
Froyok wrote: Mon Dec 06, 2021 5:35 pm gamestate.lua

Code: Select all

local SPLASH = require( "scripts.splash" )

GAME_STATE = {
Update = function(self, DeltaTime)
if self.CurrentState == "Splash" then
SPLASH:Update( DeltaTime )
end
end
}

return GAME_STATE
... make GAME_STATE local here!
Froyok
Prole
Posts: 23
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Re: Globals and singleton

pgimeno wrote: Mon Dec 06, 2021 6:58 pm Yes, except...
Froyok wrote: Mon Dec 06, 2021 5:35 pm [...]
... make GAME_STATE local here!
Ho yeah, forgot the local when writing my snippet. All good on the actual code. Thx !

Who is online

Users browsing this forum: pgimeno and 10 guests