What's the correct way of structuring your module based game

Hi, i’ve been recently wondering how big games manage module-based games.
I mean games like ‘Ink Games’ Where the game place requires modules that handle stuff like cutscenes, the current phase of the game, timers, players overall, etc.

I tried scripting an actual big game, but i’ve got into a little roadblock.

Let’s say i have 2 scripts. The Game Manager and Phase Manager, they both need to require each other to get the methods or variables from each other.

I’ve got around this problem by just passing in the GameManager instance to the .load functions in other modules.

But with that solution there’s another problem. Types. I can’t get the GameManager class without requiring it. The only way i found to fix that is by having another module just for types, but by doing it this way it’s way harder to script since if you want to add a function you have to manage 2 scripts at once.

If you’re wondering how my modules look, here’s an example:

--!strict
local GameManager = {} :: GameManager
GameManager.__index = GameManager

export type GameManager = {
   __index: GameManager,
   
   new: () -> Game
   --other methods
}

export type GameProperties = {
   --game properties here
}

export type Game = typeof(setmetatable({} :: GameProperties, GameManager))

function GameManager.new()
   return setmetatable({
   	--properties
   } :: GameProperties, GameManager)
end

return GameManager

There has to be a better and correct way of doing this.
Important Notice: I. Need. Types.

Before anyone points it out, I’m not making another version of ink game…

1 Like

Avoid cyclic dependencies between modules. They create fragile code, make debugging harder, and often lead to unexpected runtime issues. If two modules depend on each other, it’s usually a sign the design needs to be refactored into clearer, one-way dependencies.

Types are often kept in a separate module, might be a tiny bit harder to manage but it’s a million times better than trying to get around cyclic dependencies.

1 Like

Yea, that’s what im wondering about and I want to avoid doing that. I can’t seem to find a good way of doing this

1 Like

Try to design your modules so they never require the main script (or each other) again. If a function in one module needs access to something from another, pass it in as a parameter instead of creating a cyclic dependency

1 Like

Yea, passing the instances as parameters was my main go-to solution, but again it’s the types, having each type in a seperate module is hell. Thats why i wanted to avoid doing that, if there’s no other better solution than this, then i guess i need to stay with it for now

1 Like

Something I found can be useful in the right scenario is to re-export them from a different script
image

This won’t solve your issue though, getting around cyclic for the first time is a bit of a learning curve

1 Like

Yea i already know about the cyclic dependencies, im just trying to get over the fact that i need to redefine the types from another type module, it’s just annoying

1 Like

Just have some “Types” modules. This is a commonly followed pattern. Maintaining 2 scripts at once may be time consuming, but it’s worth it, and was always worth the effort. Unless you really have problems with managing scripts.

1 Like