What is the best practice for scene management for a serious project? How do commercial games set it up?

Hey, I’m creating a rouge-like card game inspired by Slay The Spire which has a bigger scope then my other projects, so I want to adhere to best industry practices.

I’ve created a prototype which contains the following scenes:

  1. Main Menu scene
  2. Options Menu scene
  3. Tutorial Menu scene
  4. Generated Dungeon scene
  5. Battle scene
  6. Event scene
  7. Shop scene
  8. Credits scene

I have a few questions:

  • Should my menu be only one scene?
  • If yes, then what is the best way to achieve that? Just disable and enable UI elements or maybe switch between multiple cameras or is there another approach?
  • Should other game scenes be in generated dungeon where I would just switch cameras between them? (Note: those scene aren’t too complex and could all fit onto the same scene)
  • Is it good to have manager scripts (game manager, player manager, audio manager, etc.) be static so that they don’t destroy on load when switching between scenes or should I have copies of them in each scene?

in my experience , most big projects have an “EntryPointScene” (or something along those lines) , and it’s a scene that gets loaded from the start of the game and never gets unloaded until shutdown , it contains most of the core systems that need to be alive during the lifetime of the game , things like AssetLoadingManger , SceneLoadingManager , SettingsManager , CameraManager (you get the point) , the scene is treated as the “root” of your game , the rest of the scenes would be loaded on top of it additively.

Now to answer your questions :

  • I dunno , should it ? most of the time there’s no dependencies between the main menu and the “gameplay” scene so i would say yes , the idea with scenes is to split your game into logically independent chuncks to avoid loading unnecessary stuff into memory , for example , do you really need to have your gameplay terrain and buildings loaded in the background during “Main menu” where all you do is click on UIs ? probably not.
  • What i personally prefer (and what i deem as a clean solution) is to have only one camera throughout your whole game (placed in the “EntryPointScene”), and switch the camera settings and controllers as you transition (Enter/Exit) between scenes
  • (I think i got what you’re trying to say even though it wasn’t too clear to me) Depends , if you want a continuous experience between the dungeons and avoid loading hiccups and spikes , then just put them all in one scene and manage camera switching and toggling manually during gameplay , else just put a loading screen whenever you want load/unload the wanted scenes
  • No , do yourself a favor and avoid that , doesn’t work well with unity’s “philosophy” , keep it component-based if possible

I could go more in depth about this , but if this is enough for you then great , if you have more questions i have a discord dedicated to helping with more indepth stuff

Hello,

I came across the same problem recently. Also came to the same conclusion to use additive scenes in first sight. Mainly because I’ve seen that in companies.
Though I don’t really use an “entry point”, mainly because I want to be able to launch every scene in dev. Not having to launch it from the splash each time I want to check something.

So I think it’s not the only solution to manage this and wanted to discuss about it.
Taking my thought and a bit of GPT I have 3-4 solutions:

1. BasicScene

Use a basic scene that contains only the DontDestroyOnLoad centralized elements
This solution is the one I’m using now. Basically my structure is:

  • Splash
  • Basic Scene
  • Lobby
  • Scene1
  • Scene2

Splash is independant, it’s intend to load the BasicScene + Lobby, then BasicScene kill itself after having initialized all DontDestroyOnLoad components (because Unity is not happy with inconsistent lights settings). So well you can consider it as the “entry point”.

If I’m in dev mode, I can load independantly for exemple BasicScene + Scene1.

My hierarchy looks like that:

BasicScene
  • GameManager => DontDestroy
    • Network
    • … all important stuff
  • Players => DontDestroy
    • Player1 (onthefly)
    • Player2 (onthefly)
Lobby / Scene1 / Scene2 / …
  • World
    • … all your worlds prefabs
  • Lobby (Pure context code)

Though I have some problems with the reference right now on game start. Need to ensure the BasicScene is loading first. Also seen a comment saying something around “If it’s common, don’t put in a scene” here Could the order of scene loading in the editor be made deterministic?

Well I am also using static .Instance for the GameManager and it seemed to work in another project so I have to double check. Imo, this solution make sens matters of infrastructure but it is really? More in a multi-dev way as if I load a scene I expect it to be full autonomous as a dev.

2. Initializer + Prefabs

Pointed by GPT as the “best” solution. Basically you put an “Initializer” script in a “Initializer” GameObject with two prefabs as reference (which is your important stuff, here is “GameManager” and “Players”). You put this in every of your scene and check if the prefabs have been initialized in the hierarchy or not.

So basically:

Lobby / …

  • Initializer
    • => ref GameManager prefab
    • => ref Players prefab
  • World
  • Lobby

then

  • GameManager
  • Players
  • World
  • Lobby

I didn’t test it so I don’t know if some problems would get along but it seems to make sens for me. Probably need to AutoKill Initializer so you don’t stack 24 Initializer in your root. This is a bit redundant.

3. AutoKill

Basically I just though about adding something like a script AutoKiller in the GameManager and Players. It’s a bit the same as solution 2 but you don’t necessarly need to create prefabs.
If it find in the hierarchy a same name / same tagged / or same script, it destroy itself in the load. Something like:

public class Autokill : MonoBehaviour { 
    void Start {
        // Or check for a specific tag in hierarchy
        if (transform.root.GetComponentsInChildren<GameManager>() > 0){
             Destroy(gameobject);
        }
    }
}

(This could be a bit more generalized to use a referenced class in inspector or a tag)
Be careful using gameobject name comparison because it is error prone.

Probably a bit redundant as solution 2.


Well in any way, the main point is to avoid having doubles and useless reinitialization. And well errors…

Still searching for the best solution among 1. and 2.

Now about the author @JanZagar question on menu, Imo, (especially if we keep solution 1. with additive scenes) I think it depends on the menu. 3 exemples:

  • Introduction Menu : If it’s just a scene to load UI as a “hub”, or a functionality only in this scene, (for me it’s the lobby scene that contains a menu to connect to a server), I think it make sens to put it in the scene itself => Menu = One associated scene
  • Option/Exit Menu : Goes in the “Players” (holders of each Player instance) if you want to have one menu for everyone. Or in the “Player” if you want to be able to pause independantly on local multiplayer
  • Inventory Menu : Goes in the “Player” prefab (especially if you have local multi with new input system)