[Tutorial] How To Use Global Functions & Variables to Simplify Development

Hi guys, this is my first tutorial on Core so please go easy on me! :smiley:

Today, I'm going to briefly go over the implications of _G. Lua's _G is an incredibly powerful tool and is often overlooked by newer developers due to it not really being complex enough to be talked about frequently. _G is a global table, with a reference built in & accessible to any script executed within a specific VM. If you're not sure what that means, let me explain.

Using _G

To start with, you should know that _G is an empty table to start with. When the first lua script runs, _G will have no information. That means that any script that needs to read global variable should wait until it is certain it is there instead of immediately trying to access it.

To start, we are going to create an "_G Manager". This script will set up our _G table and expose some variables and functions for our other scripts to use.

_G Manager:
_G.gameState = "Menu"
_G.SomeRandomVariableName = 6

_G.functionOne = function()
    -- this is one way to define a function inside of _G, with the name of functionOne
end

function _G.functionTwo()
    -- this is a different way to define a function inside of _G. Both ways work fine.
end

_G.isLoaded = true -- we put this here so that our other scripts can listen for this to be set to know that all of the functions in the _G table have been properly loaded.
-- This code *would* do nothing, but because we assigned the variables in _G, we will see that they, in fact, are accessible from other scripts now.

Now it's time to access our data. In order to do this, we're going to make a new script. At the top of that script, we are going to add a wait. This is only necessary if you are accessing the data immediately. If it's being accessed when a trigger is pressed or something, don't worry about adding the wait because the chances are the table has already been loaded.

while _G.isLoaded ~= true do -- If the _G manager was executed first, we shouldn't wait at all, so by using a while loop we can skip the whole thing if the initial case is false. Side note: ~= is the "not equal" operator in Lua.
    Task.Wait()
end

print(_G.gameState) -- prints("Menu")
_G.gameState = "Game" -- sets the global variable equal to "Game". This will also update it for any other script.

_G.functionOne() -- this will call functionOne from the _G table.

Use Cases

While this isn't really a useful tool for building assets for community content (assigning the same name in _G as something the developer is setting will cause an overwrite, so you should use something local for that like modules or events) it is incredibly useful as a game developer who wants to keep everything in one place.

Some examples of what I've kept in _G on my projects are:

  • Text manipulation libraries (put commas in the thousands, print tables, scan for a text match, etc)
  • Caching network data (player progression stuff for UI, etc to save bandwidth)
  • Custom Notification functions (the long code for tweening UI and flashing colors can clutter up other scripts and is more difficult to change in the long run)
  • Code that is expected to be reusable

So there you have it guys, that's _G. It's a fairly simple concept but when used properly it can really change the way you structure your games. I hope this has helped! A quick note before you go: There is another global table called "shared". It works identically to _G but I'm not sure it exists in Core, it might exist in other versions / forks of Lua, but I thought I'd include it here just as another tool to add to the toolbox in case any of you are working on non-core Lua projects as well!

If you liked the post, please leave an upvote / like! I appreciate it! :wink:

6 Likes

what do you think about, making a videogame a creating a subtable to manage your own game functions.

Like
_G.MyGame = {}

I would like to know more about this global stuff because, sometimes trying to manage objects in core might become really uncomfortable...

for example, you create an object "Bomb", how can you tell core your "Bomb" is a "Bomb" if in fact, it is just a folder.

There are a lot of things I would like to try with the _G in my videogame, thank you for the info by the way:P

1 Like

Hey! Sorry for the late response, I haven't been as active here recently.

Using _G Subtables is behavior I would absolutely encourage, especially when projects start getting very big. I use these all the time in my projects because they allow me to group similar behaviors and remember less keys. For instance, when using a text library, you might list all these functions in the Text subtable:

  • _G.Text.formatNumber()
  • _G.Text.parseJSON()
  • _G.Text.spliceText()

A particular case where I've used this behavior is with accessing player data. I will admit, I am not hugely familiar with how data works in Core, but I am familiar with how it works in Roblox. In Roblox, we are limited to how many times we can "Get" and "Set" the player's data. For that reason, I store all the player data in one big table, and then get it when they join, store it locally, and set it when they leave (also some autosaves in there). Without using _G functions to abstract the parsing of the data table, the gameplay scripts would need to receive a huge table and parse it themselves with something like this

data["Profile1"]["inventory"]["vehicles"]

just to read what part the player has saved in the hood spot of their car. Instead, it is much more efficient and scalable (and uses less bandwidth) to just request only what we need. With a single script who manages the data system, we can define an _G library that will allow us to index this data more efficiently.

_G.Data.getData(player, "inventory.vehicles")

You can also set this way
_G.Data.setData(player, "stats.money", 1000)

And if you want, you can supply a whitelist on the server for the data keys that are non-destructive for the user to save themselves - like controls - and then create a client-friendly copy of the function that only lets them set certain whitelisted keys in their own datastore, like so:

_G.Data.setData("controls.steerleft", keys[23])

and if they try to set something like their money:
_G.Data.setData("stats.money", 1000)
it errors because the client alias of the setData function does not allow 'setting' the stats category on the data table.

This is a really long response but I wanted it to be thorough to make sure that anyone else who reads this can see some implementations of other use cases where this works.

2 Likes