Best way to initialize HUD GameObjects?

I’m working on my first videogame using Unity. I have a screenspace - camera canvas called HUD to which all other interface objects (health, ammo and FPS counters, etc.) are parented.
The point is, since there are going to be many scenes in my game and I don’t want to recreate this HUD GameObject over and over, *what is the best way to ensure there’s always ONE HUD object in the scene?

I thought I could place it in the title screen scene (along with other similar objects that need to be in every scene like the GameManager) and then set it to a DontDestroyOnLoad. But that means that if I’m testing a specific scene I have to copy the HUD from the title scene to the current scene or I won’t have it.

What’s the best solution in these cases?

Additive scene loading is one possible solution:

A multi-scene loader thingy:

My typical Scene Loader:

Other notes on additive scene loading:

Timing of scene loading:

Also, if something exists only in one scene, DO NOT MAKE A PREFAB out of it. It’s a waste of time and needlessly splits your work between two files, the prefab and the scene, leading to many possible errors and edge cases.

Two similar examples of checking if everything is ready to go:

Alternately, make the HUD come into existence as a Prefab or scene-driven Singleton, using a pattern like the second option here:

Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

Thank you for the quick and useful answer. I’ll definitely be going with additive scene loading. I have a couple of questions though.

I was thinking of splitting GOs and scenes like this:
HUD scene: with a canvas for all HUD stuff
Camera scene: has the main camera
EventSystem scene: has the EventSystem GameObject

1- I have a GameManager GameObject (programmed as a singleton) that sets itself as DontDestroyOnLoad upon Awake(). Should I set it in a separate scene or not?
2 - The HUD canvas is set to Screen Space - Camera, so it depends on a render camera. The problem is that I need the camera on a separate scene because the camera is always needed but the HUD is not (main menu). This creates a cross scene reference which must be avoided. How do I solve this?
3 - Since the game needs to be saved and then loaded, and some values of the player need to be saved across scenes and game sessions as well, how do I spawn the player and apply the values loaded from the save file? Do I create a player prefab, a spawnpoint object in every game scene and then instantiate the prefab, set its values from the save file, and set its position on the spawnpoint object, or is there any simpler way?

I’ll preface this in saying that this is all my opinion on this sort of stuff.

1: Kind of up to you, but if you do have it in its own scene that’s additively loaded, then most likely DontDestroyOnLoad is no longer needed (so long as you’re moving through levels additively as well). DontDestroyOnLoad is kind of its own scene that is persisted been singular scene loads.

2: Do you mean the UI is set to Screen Space - Camera? If that’s the case, you can probably set the render camera with Camera.main on Awake/Start: Unity - Scripting API: Camera.main

3: I would personally make the player it’s own scene as well. Though to answer the question of saving and loading… that’s a very large kettle of fish, honestly. It will depend on the specifics of your game and what sort of data you need to save.

In my own project, each level scene (not scenes like the player scene, UI scenes) has a scene manager scriptable object referenced in a scene manager gameobject (which just wraps around the SO and gives it access to monobehaviour functions). On awake this container updates the player save file with what scene the player is in, so that come time to load the game, it knows what level scene to load and where to put the player.

Naturally it’s many, many layers more complicated than how I’ve described it, but as I said, saving and loading is a tough learning point for new devs, myself included, and it’s taken me some good time to get my bearings about it. It’s important to keep in mind that you should be thinking about how data should be saved at much as possible, and probably get a basic save/load system working as early as possible too.

Hopefully my rambling helps.

I would not even put it in a scene. I would make it a pure Unity singleton (see below) that comes into being either by simply accessing it, or else by calling an InitGame method.

Why not make a special UI camera in the scene with UI? Use layers to keep it from seeing everything else, and vice-versa.

This is always a bespoke solution (totally dependent on your game) and really only incidentally affects the additive scene loading choice.

Here are my notes on standing up this sort of a persistent system… start small and build up on it, testing EXTENSIVELY at each step of the way:

Load/Save steps:

Don’t use the binary formatter/serializer: it is insecure, it cannot be made secure, and it makes debugging very difficult, plus it actually will NOT prevent people from modifying your save data on their computers.

When loading, you can never re-create a MonoBehaviour or ScriptableObject instance directly from JSON. The reason is they are hybrid C# and native engine objects, and when the JSON package calls new to make one, it cannot make the native engine portion of the object.

Instead you must first create the MonoBehaviour using AddComponent() on a GameObject instance, or use ScriptableObject.CreateInstance() to make your SO, then use the appropriate JSON “populate object” call to fill in its public fields.


ULTRA-simple static solution to a GameManager:

OR for a more-complex “lives as a MonoBehaviour” solution…

Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

Liked the idea of the special UI camera. I’ve moved the HUD canvas to the HUD scene and added a camera set to overlay render mode there, then set the canvas camera to that camera. However, the main camera of the scene (set to base render mode) needs to have a reference to the UI camera (still the same issue) (I’m looking at the Stack section in the inspector, I don’t know how to actually do this, but I suppose this is how it’s done).

Do I need a script that sets the reference everytime a new scene loads?

I like to make lots of lazy locators that find stuff when it becomes available, usually finding it either by code instance in some custom class, or just using FindObjectOfType(); for instance.

This also requires you to write code that can behave for at least a few frames while things load up, eg, not crashing or throwing off null references if one scene loads a frame or two later.

The most common place I do this is with the player scene, who needs the content scene to load fully before he can find where in that scene he needs to spawn. The player scene will load and do nothing, just looking either for something called MySpawnPointMarker or else interoperating with a spawnpoint manager of some kind.

Similarly you can make the scripts behave when something goes away, such as a radar screen that shows the area around a player, but when the player dies and gets destroyed, the radar screen doesn’t start freaking out, it just patiently waits for the new player to appear.

What about a SceneLoader GO that sits in every level scenes that simply holds a list of names of scenes that must be loaded along with the level, then just iterates through them and loads them if unloaded, and unloads the ones not present in the list? Somebody suggested something similar to this before, but I don’t get why I should use a Scriptable Object (never used them and not used to how they work). Can’t I just make a prefab and put it in every game level?

By someone, I assume you’re referring to me; nonetheless Scriptable Objects exist on the asset level, and are thus scene independent. In particular, so long as they’re referenced by something their data will persist throughout scene changes (in a build; in editor their data persists regardless). Their usage is a bit of a preference thing, but the fact they’re an asset that can hold its own logic makes them rather useful.

On the topic of scene loading, I use a scriptable object to wrap around a scene Addressable reference, with functionality that handles loading and unloading the scene. Anything that either needs to load the scene, or know if the scene is/isn’t loaded can then reference this scriptable object asset.

For example, the player has a reference to the inventory UI scene object, which it uses to load the scene upon the first time the player opens their inventory. However, my pause menu also references this object, and can check if the inventory is open underneath the pause menu, so it knows not to turn the players movement inputs back on. Things like that one thing (among many) that scriptable objects are useful for.

Again, their usage is ultimately up to you. I’m on the extreme end of how much I use Scriptable Objects.

If your UI elements are specific to say, the player, then their initialisation could be done by a component on the player, or a game object in the player scene. I don’t think there is a need to put a particular prefab in every scene, as this would no doubt be prone to error.

What’s a scene addressable reference? Also, isn’t it bad loading a scene only when it is first requested (I think it’s called lazy inizialization)? Wouldn’t that cause a sudden slowdown when you open the inventory for the first time? Or is the scene loading fast enough to be kept syncronous since it is only containing an UI object?

I don’t think I get what you mean (still a beginner in Unity). Why a scriptable object? Because if I want to change the lists of scenes that need to be loaded every time I can just change the SO? But wouldn’t a prefab be simpler to use for that purpose?

Also, you talked about referencing the scene object. I tried writing something like public Scene scene; in a MonoBehaviour, but even if it appears in the inspector, I cannot assign any value to it. I must be doing something very wrong.

Addressables is a Unity package that lets you load scenes, assets, etc, asynchronously (more info here). An addressable reference is a reference to a scene that you have in one of your addressable groups, which is handy, because - as you’ve discovered - you can’t normally have Scenes visible in the inspector (so you’re not doing anything wrong).

The packages takes a little bit to learn (there are some basic tutorials on Unity learn), but you only need to know the basics to do the typical stuff of loading scenes and assets. As mentioned, it’s done asynchronously as as not to block the main thread, which does take a bit of learning to understand async code. It can, of course, be done synchronously if you need that as well.

Pretty much all my scenes are loaded via addressables (including UI scenes). The only scene in my build list is the main menu scene.

And apologies, by scene object I meant my scriptable object I use to hold an addressable reference to a scene, what I’ve called a ‘Scene Data Object’. You can’t very easily track whether an addressable reference has been loaded, so my Scene Data Object keeps track of that alongside handling the logic of loading and unloading the scene.

You of course can’t reference scenes objects between scenes, but you can reference a scriptable object asset across as many scenes/prefabs as you want, hence their usefulness in when I need multiple different things across multiple scenes to know whether a particular scenes is loaded or not.

Watch this video should you have a chance. It should give you an idea of the basic ways a scriptable object can be used:

And if you’re new to this (like I am, I’ve only been doing this about 8 months) don’t worry if you don’t understand stuff right away. My advice, get what you want working however you know how to get working. As you learn more you’ll figure out new, possibly better ways to achieve things and can always go back and refactor your code/architecture. Keep at it!

I’ll stop rambling about scriptable objects now ;p

Alright, thanks to the help of both of you, I managed, after a couple days of thought and work, to come up with a system that SHOULD do it.

7529873--929567--upload_2021-9-28_15-38-40.png
I have a SceneLoader prefab that keeps track of all the additive scenes that need to be loaded in this level. Upon load, it just checks which ones are already loaded and which ones are not, and loads scenes that need to be loaded and unloads scenes that shouldn’t be loaded.
7529873--929573--upload_2021-9-28_15-41-56.png

This is the code for it:

using UnityEngine;
using UnityEngine.SceneManagement;
using System;
using System.Collections;
using System.Collections.Generic;

public enum AdditiveScene
{
    HUD, Camera, EventSystem
}

public class SceneLoader : MonoBehaviour
{
    public List<AdditiveScene> scenes;

    public IEnumerator Start()
    {
        var animator = GetComponent<Animator>();

        foreach(AdditiveScene scene in Enum.GetValues(typeof(AdditiveScene)))
        {
            string name = scene.ToString();

            if (scenes.Contains(scene)) // Se la scena va caricata
            {
                if (!SceneManager.GetSceneByName(name).IsValid()) // Se la scena non è gia caricata, caricala
                    SceneManager.LoadScene(name, LoadSceneMode.Additive);
            }  
            else // Se la scena non va caricata
            {
                if (SceneManager.GetSceneByName(name).IsValid()) // Se la scena è gia caricata, scaricala
                    SceneManager.UnloadSceneAsync(name);
            }

        }

        yield return null;

        SceneManager.SetActiveScene(gameObject.scene);
        animator.SetTrigger("disappear"); // Fai scomparire la schermata di caricamento
    }
}

Reguarding the first image, the HUD and camera are still in the level scene because I haven’t yet finished writing some code to make camera-depentant objects behave even without the camera.
I did do it for the WeaponHUD script, that depends on a reference to the player (don’t know if this is a good way to do it, but it’s the simplest one I could invent).

    // Se non c'è un giocatore, cercalo
    void Update()
    {
        if (player_ == null)
        {
            var playerTest = GameObject.FindWithTag("Player");
            if (playerTest != null) player = playerTest.GetComponent<Player>();
        }
    }

What are your thoughts on this?

7529873--929573--upload_2021-9-28_15-41-56.png

It’s useful information.

My post?