Advice on program architecture & script structuring?

Hello.
Im me and a friend is working on a little game project over the summer and are looking for advice on how to structure the code in our project.

The game is a top-down building & management kind of game so naturally there is gonna be some functionality which cannot directly be related to objects, As we’ve found out this tends to lead to such scipts being put a little bit of everywhere.

At the moment we have kind of a “godobject” where we attach scripts that doesn’t really need to be on any specific object (such as Simulation, Input manager) - i have a feeling that this kind of way to design isn’t really they way it’s intended in Unity.

So my question then is, How do you guys go about the general architecture of your programs?

If you have a script that isn’t related to a GameObject, there is no need for the script to be a MonoBehaviour. you can use a normal C# class.

This class will still probably be constructed from a GameObject, because the way to run code in Unity is generally through Start(), Update(), etc. Most of my games do have a “GameManager” game object that acts kinda like Main() function in a non-Unity C# app. Most of my code however are C# classes that do not derive from MonoBehaviour.

In other words, if you need a Transform, Start(), Update(), or something similar, use a MonoBehaviour. If not, use a regular C# class.

The “issue” is that all the scripts sitting on the Godobject does need monobehavior, for example “Simulation” is responsible for keeping the simulation coroutine up, which in turn pings other objects to update stuff.

Maybe my issue is just that I don’t like the way it’s supposed to be done?

I don’t see the problem with this “godobject”.

I have one, it’s usually called ‘GameName.Game’, like ‘Vivarium.Game’. And I attach my ‘Game’, ‘InputManager’, ‘SceneManager’, etc scripts to it.

I wouldn’t necessarily call it a god object… a god object is an object that does to many things, it represents the data of a vast majority of the program:

My InputManager doesn’t know do too many things. It handles Input, it’s pretty straight forward. Nor the SceneManager, it manages transitions between scenes… pretty straight forward. Even my ‘Game’ script isn’t a god object. It’s a very basic representation of the game (really it just performs the setup code, creating the InputManager and SceneManager and what not in the boot scene).

Sure they’re all scripts connected to a single GameObject… but don’t confuse a GameObject with the sort of object they’re speaking of as a ‘god object’. That pertains to a class instance… a GameObject as a class instance is just a GameObject, the scripts attached are their own unique Objects as well. The logic and state of a GameObject is not god-like.

Now if you have a script that does way too much, it itself could be a god object. I don’t know what ‘Simulation’ does, but if it represents all the logic of your game… well yeah, that might actually be a god-object. But it being on a GameObject with InputManager doesn’t make it a god-object, it’s the fact it does too much.

An example of a god-object would be like a ‘GameManager’ script that does something like this in its update:

    public string currentSceneName;
    public GameObject player;

    void Update()
    {
        switch(currentSceneName)
        {
            case "Level1":
            {
                if(player.GetComponent<HealthMeter>().Health <= 0)
                {
                    GameObject.Destroy(player);
                    currentSceneName = "GameOver";
                    SceneManager.LoadScene("GameOver");
                }
            }
            case "Level2":
            {
                //...
            }
            case "GameOver":
            {
                if(Input.GetButtonDown("Action"))
                    Application.Quit();
            }
        }

    }

This is a god-object. It’s just doing way too much, and persists on accounting for the state of the game at any given moment.

Instead of breaking out level 1’s logic into its own script, and creating that object when the game enters that state. It instead wants to have a variable that names that possible state of the game, and performs actions based on it.

There is of course other examples, but this is like one of the most common god-objects I’ve seen people implement.

Ok, Godobject was perhaps not exacly what i meant, maybe it’s just me thinking of this the wrong way but it seems like a poor design choice (by me) when i keep piling up more and more scripts on the same object, which is why I thought i had to rethink my design so I wont have to regret it later.

As for the Simulation script. it’s task it to keep the simulation going, every “tick” it keeps a list of active objects that needs updates every tick, and so it updates them on needed information every tick.

Here’s a screenshot showing the different GameObjects I have in a prototype. This is a prototype so the Hierarchy is a bit sloppy, but you can see it’s fairly normal to have GameObjects sitting in the scene. I’ve worked on published games that might have three or four times as many “Managers” sitting in the scene. (Eg: TextureCacheManager, PlayerManager, InventorySingleton, AdManager, etc…)

It’s okay to apply many (hopefully smaller) scripts to the same object. Unity is kind of based on the Composite Pattern. (Of course there is a reasonable limit to this. See this Unity blog post titled “10000 Update() calls”.)

Try your best to make your project as modular as possible. Each module should be self contained and have very few dependencies with other modules. This isolation is important for the overall understanding of your code and will make debugging easier, since a bug would be local and would not spread easily.

A class should have a minimum of responsibilities. When a class is starting to do a lot of things, it’s a a good sign that it should be split into more classes. Also when the code start to be less readable and simply does not make sens, it’s time to refactor it.

A god class (often called a manager) is not necessarily a bad thing and it is often required. Though, it does not mean that this type of class should do everything, rather it should be some sort of overseer that would delegate must of the work to more specialized classes.