Unsure about whether my way of structuring Scenes is good

Godot Version

4.4

So obviously there isn’t a one-size fits all solution to how scenes should be structured, regardless I moreso am asking this out of curiousity and perhaps reassurance that i’m not doing anything too crazy here. My project is an Action RPG but im also helping a friend with a platformer wherein I reuse a lot of the same principles from my main project due to reused code and familiarity.`

With the use of Nodes, I often separate my game into a bunch of states with self contained code. If we use a Traditional RPG as an example, your states would be something like;

  • Title
  • Overworld
  • Battle

Each represented with their own Nodes with their own self contained scripts for handling things within them; e.g the Overworld Node is designed to load scenes from a unique “Map” Resource which contains a reference to the Scene to assign as a child, spawning nodes, etc.

With that said, my game only really has two states at the moment, Attract Mode node (consisting of splashcreens, title screen, menu, etc as children and controlled by the Attract Mode node’s code), and a Game node which i’ll be focusing on here and looks something like this;

  • Game (Node - Handles all code related to this portion of the game, e.g spawning character, etc)
    ** UI (Control - reads data from the Game node)
    ** Camera (Camera3D - its not a child of the player so that I can control camera movement by code, allowing for easing, focusing on other objects if need be, etc)
    ** PlayerCharacter (CharacterBody3D - spawned by the game node, assigned to variable “PlayerCharacter” in Game node)
    ** World (Node3D - is assigned a “Map Resource” containing a reference to a Scene to load as a child of itself, position the player to the defined spawn position, etc)

For the most part… this is almost certainly an extension of what is sorta described here.

So my confusion comes from this section;

If at all possible, you should design scenes to have no dependencies. That is, you should create scenes that keep everything they need within themselves.

If a scene must interact with an external context, experienced developers recommend the use of Dependency Injection. This technique involves having a high-level API provide the dependencies of the low-level API. Why do this? Because classes which rely on their external environment can inadvertently trigger bugs and unexpected behavior.

This has made me concerned that my way of going about things is perhaps not the best way of working in Godot and its doubt that had set in when I studied other open source Godot projects and tutorials which would often do things like for instance…

  • Have each level contain a copy of the Player Node.
  • Have the Camera and Game UI be children of the Player Node. (as opposed to my approach where Camera and UI are children of the game state which has a player character which can be assigned and reassigned)
  • Each level contains all the necessary code to run the basic gameplay.
  • Use Globals for persistent elements like stats.

This approach feels a bit disorganised for me as someone who prefers breaking things down into nearly self contained sub-sections and I feel its wasteful to keep gameplay-specific persistent elements loaded even when they aren’t needed, but I do admit that it does line up closer to what the docs recommend concerning dependencies as you could even test a level by loading the scene itself whereas mine requires the presence of certain nodes to be present to make sure the game is ‘playable’.

So, I wish to get input from others here, how am I supposed to interpret “don’t have no dependencies”. Am I supposed to interpret it as the game being able to theoretically function from any scene whatsoever even if its just got some default values? Or is it just a case of ensuring that individual scenes don’t crash simply cause the code expects the presence of certain nodes (e.g my old camera implementation where both the character and player were co-dependent on each other)

I think you are missing a crucial part here.

If at all possible, …

This does not mean absolutely no dependencies whatsoever. It just means when you are introducing a dependency make sure it is really needed and not just a hacky short cut.

Your original plans were sound. Do not create copies of Player Nodes etc. This is a ‘fair enough’ dependency. The only thing you should do is try to deal with missing dependencies as gracefully as possible. Even if at the last resort that means throwing and error and saying “This scene cannot run without the parent ‘GameManager’ node.”. And of course, don’t search the tree blindly, dependencies should be set with signals or setters.

So yes, it is better if scenes can have no dependencies, but that is not always possible. Having a dependency is not a crime, but to avoid bad coding practices and spaghetti code there really should not be a reason for too many. It is like trying to avoid confrontation, certainly it is preferable to avoid it, but sometimes . . . .

1 Like

To me your organization looks better than the ones you’re describing. GUI as a child of the player and stuff like that sounds pretty bad, but maybe, for example, the UI changes based on the player character or the weapon used. You cannot just take a design decision in isolation.

Some potential problems in your design based on the decription are stuff like “UI reads data from Game node”. Rather, the Game node gives data to the UI. Your UI should have functions like “update this” and “update that”, which are simply called by whoever wants to update the UI. Then your UI should have signals like skillChanged, which are listened to by whoever wants to.

Scenes never read data or call functions on their parents or siblings. If this is needed, then these nodes are given to the scene by their parent. This is what dependency injection simply means. Ideally, all the communication is done with the scene root by functions and signals.

Yes, this often creates more work and more indirection. It’s up to you if it’s worth it. If your design will never change, then it’s better to have everything in one big scene.

1 Like