Coming up with a solution for managing UI elements across different scenes wasn't as trivial as I first expected. It's very easy to attach a canvas to a scene and call it a day. Initially I had all of my canvases attached to the first scene I expected the player to enter and then simply call DontDestroy(gameObject) on all of the canvas scripts.
So what's the issue? If you allow the user to save and load from other areas, you have to copy/paste a prefab into each of your other scene. This is a trivially easy solution, but it's too dirty for my liking, instead we should dynamically create and manage the UI elements via a UI manager.
The first step would be to create an empty object in our loading screen (the loading screen will always be accessed before gameplay starts).
In this case, since we're also going to be appending a bunch of canvases to this empty object, it needs a canvas or stuff just doesn't render properly. Also make sure to add a reference to your UI element prefabs:
And the start script for the UiManager script:
void Start()
{
if (allCanvases == null)
{
DontDestroyOnLoad(gameObject);
foreach (GameObject prefab in prefabsToInst)
{
GameObject toAdd = Instantiate(prefab);
toAdd.name = prefab.name;
toAdd.transform.SetParent(transform);
uiCanvases.Add(toAdd);
}
}
else
{
Destroy(gameObject);
}
}
Each canvas appends itself to the UIManager and therefore does not get destroyed on scene change. All specific behavior for each individual canvas can be handled in the individual UI component's scripts. Blamo! That's it! Crisis averted, we can now navigate between scenes and know that our UI manager is now empowered to show canvases, hide them, etc.