How do you program a “level”?
This is not usually a problem in a game engine, but Unity requires you to isolate each “level” as a Scene. And Unity’s Scenes have some strange design choices (bugs?) – IMHO they were designed for toy projects, not for production games.
Here’s a stream-of-consciousness on what works, what doesn’t, and how I can workaround / do it better within Unity… it’s not carefully researched, mostly it’s from memory / off the top of my head. So … it might not all be true :).
When I switch to Unreal4, I’ll be using posts like this as a baseline to compare where Unity failed, and how badly – or vice versa.
UPDATE: added some more approaches and ideas/commentary
- When you load a Scene, everything in your game is destroyed
- Scenes cannot take parameters
- Scenes cannot hold state
- Scenes cannot be constructed – nor modified – in code. Procedural game? HA! No chance!
- You can’t edit more than one Scene at once (this is absurd, and makes development unnecessarily painful)
- … and a bunch of other problems, many of which are side-effects of those above
More Unity bugs that make this Hellish
- When a Scene loads, there is no way to tell Unity what code to run.
- This is REQUIRED because Unity refuses to let you pass parameters
- Without it, you have to rely upon Awake(), Start(), and OnEnable()
- Those three methods are DEFINED as running in “random” order among your objects
- If you have a parameter that needs to be found, loaded, and positioned, you can put it in Awake()
- If you need a global reference to that parameter, you’ll have to set it during Start(), otherwise there’s no guarantee it’s been found yet
- Any code that needs to use that parameter cannot go in anything except OnEnable()
- …BUT: a lot of (most?) Unity code that should be in Start() will break if it’s in OnEnable(). The two methods do different things!
Unity’s scene-loading SUCKS.
Some common workarounds
Unity5: additive loading
You can load one Scene, and then load another over-the-top. This has long existed in Unity Pro to some extent (allowing “level loader” Scenes etc), but U5 upgrades/extends it a bit.
As I understand it, Unity5 does NOT fix the problems, but it does mitigate them. In particular, I’ve looked at Editor changes to improve handling for loading multiple scenes at once. I haven’t experimented with this yet myself (I’m still on U4). From the videos I’ve seen, it looks like a definite improvement, but still quite a long way behind what I’d call “standard” in game-engines.
If you’re able to use U5, I’d start with this, and see how far you can get with it. And then post / tweet about your tips and tricks, please.
Don’t Destroy On Load
You can mark any number of GameObjects as “Hey, Unity! When you delete everything (which is stupid, but OK), don’t delete THIS one!”
In theory, that means you can write your own mini game-engine (um, why are you using Unity again? Because you want to re-invent the wheel all the time, right? /sarcasm) that stores all the game-data in a “don’t destroy” object, and then parses and loads it after the new Scene has finished loading.
This doesn’t solve anything – it shifts the problems to a new place, but at least they’re now in YOUR code, instead of in Unity’s. You end up writing your own mini game-engine. You get almost zero help from Unity, and … to make it harder … you have to remain compatible with Unity’s proprietary data-handing: Initialization protocols, crappy Serialization, etc.
This is the “real” way of doing DontDestroyOnLoad, the standard approach that is built-in to the programming language. It always struck me as bizarre that Unity added DontDestroyOnLoad when there’s a more standard approach already in place. Was it a junior programmer who didn’t know how to use the “static” keyword? Or is there something nasty buried in Unity’s core engine that breaks on statics? (hint: I can almost guarantee it would be a bug somewhere in the Serialization system, which I detest, in case you hadn’t noticed already ;). Almost everything nasty in Unity’s architecture comes back to that monster!)
It has always seemed to work fine for me. But it’s scary: when will it stop working? How will you know? What Unity bugs are lurking here?
Take all the game-logic that WOULD have handed data to your Scene as parameters (in any normal engine) … and embed that INSIDE the Scene, instead.
When the Scene loads, it runs this logic, and goes “Oh! I seem to be missing lots of core data. I’d better recreate it on the fly!”.
It’s a tortuous kind of logic, and it can be very confusing for new devs joining your team (and for yourself, if you take a break from the project and return later). But it has some significant benefits:
- You can run a Scene in-game … OR … run it directly from Unity Editor, and it will behave (almost) identically. Testing + Debugging can be a lot quicker this way
- Scenes remain self-contained. This is how Unity’s designers “intended” you to use Scenes (although it’s a really bad solution for Games). This has minor benefits with things all the way through to source-control and multi-user editing of your game: if you work this way, you’ll encounter slightly fewer of Unity’s core bugs.
- In theory: more of the “Scene specific” code is located within the Scene (in Unity Editor). When your project gets large, in theory: this makes it easier to understand what’s going on, because the code + objects are local to each other. I’m not entirely convinced by this: with Unity’s poor arrangement of Code + Data … In practice, this locality might not be noticeable(the Unity Editor “hides” the locality, so you as developer get little benefit).
Well, I’ve met people who claim to do it this way – make your Scene in Editor, then convert everything to Prefabs, then load the Prefabs when Scene loads, passing in a different list of “names of prefabs to load”.
Oh man, please, no!
Firstly: Prefabs in Unity are badly broken for anything complex like this. They don’t work, you should be avoiding them as much as possible – except for the simplest of use cases (non-nested, simple, small prefabs).
Secondly: wow, this is tortuous. You’re abusing the “templated object system” to get around design-bugs in the “data layer” and “scene-loader”. It may be genius – but it’s the kind of genius that’s so unusual it’s prone to get broken in a future update to the engine, and Unity could legitimately say: “Wat? Why were you even doing that?”.
If it works for you, great! But for me, this was always a mind-**** too far. Possibly I’m missing out, and this is a much better route than I realised. Mostly, it’s the fact that Unity can’t handle Prefabs that puts me off. U5 allegedly fixes some prefab bugs, so … I’ll re-consider this at some point. However, the history of Prefabs in Unity is ugly, and I’ll be very cautious about trusting them.
Problems with these workarounds
Unity doesn’t support Dictionary/Hashtable
This is an enormous stinking problem (throughout Unity!).
It changes the balance of pros/cons in the above workarounds. You cannot simply add named parameters to your static / DontDestroyOnLoad / etc – because Unity will piss all over the programming language and delete/destroy/corrupt them.
(did I say I hate Unity’s broken-from-day-1 Serialization system yet? Yeah, well: I do)
In the end, I’ve found that the most attractive part of DontDestroyOnLoad is that you can use:
GameObject param1 = ... ; GameObject persistentObject = ... ; param1.transform.parent = persistentObject.transform; //param1.tag = ... oh, no - can't do this. Unity's tags are hardcoded (that is NOT what Tag means, guys!) param1.name = "Parameter 1";
You can then retrieve it later using something like:
GameObject persistentObject = ... ; GameObject param1 = persistentObject.Find( "Parameter 1" );
(assuming you get a reference to the persistent object somehow … e.g. make it a Singleton)
I believe you can do this with static objects too (?) but … having a whole object graph hiding in a static member variable is getting into the realm of “this will probably expose bugs in Unity Serialization”. Best to avoid it: life is too short to debug Unity.
Ideal / Correct solutions
Data is Data
We come back to the recurring flaw in Unity that undermines so much of the engine: Unity refuses to acknowledge that “data” is “data”. It keeps insisting: “No, no! Data is code and objects and graphics and a physics object and … and … and … and …”.
(Let’s be clear: experienced programmers look down on Unity’s approach and are generally scathing. This isn’t fanboism – the Unity approach to programming largely died in the 1980s, for good reasons. It’s unhelpfully restrictive, and quickly proved very difficult to create complex programs. e.g. Video Games)
Ideally, we would store our game’s Data as data, and hand that data to the Scene when it loads. Unity blocks us from doing that. Everything we do to emulate this (e.g. embedding a JSON (de-)serializer) is a workaround for Unity’s road-blocks.
I’ve tried most of the above approaches with previous Unity projects, and none have worked out particularly well.
What to try this time?
Here’s my current needs:
- When the Scene loads, I need to inject the player-character. This is a complex object including rendering, physics, procedural meshes
- When the Scene loads, I need to separately configure some invisible state: the player’s characteristics (e.g. Hitpoints, Score) … which the level clones, and uses as visible state (hitpoints during the level change; hitpoints when you started the level is a “saved” value that doesn’t change)
- When the Scene loads, … I also have invisible state that is used to generate other state. e.g. information about how much of the level has been explored to date. Fog-of-war, etc.
- When the player finishes the level, I need to cherry-pick some data to save out to the main menu, and to save to disk
- When the player finishes the level, I need to store the differences between “before” and “after”, so that I can award various Badges/Achievements, and one-time rewards, etc. Also: so that I can render “this is what you achieved!” end-screen
I’ve given up: I’m writing my own mini-engine to workaround Unity’s awful scene-management. Looking back over my many Unity projects, I’ve lost many days, probably more than a week, on having to constantly re-implement this core feature because Unity’s version is so FUBAR.
It’s one of those bugs that makes me think Unity’s tech leadership never write games themselves; if they did, they’d refuse to put up with crud like this.
- Write a class-load hook (using C#) to jump in every time a Scene starts loading
- Debug all the undocumented crap that Unity does when loading a Scene. This could take a long time.
- Reverse engineer what parts of a Unity scene are “Valid” and when.
- Write the kind of code that every commercial game engine (except Unity!) has for managing scene-loading
- Wrap it up in an API
- Never deal with this turd that is Unity’s scene system ever, ever again!
“Diff” two GameObjects
This would be fantastically useful. It would make many of the workarounds above much easier
It also exists, built-in to Unity. We know this: the Serialization system relies upon it.
But they don’t allow us access (as far as I can tell: I’ve not seen a public API call / set of calls for this?)
“Clone” a GameObject, keeping a reference to the original
All Objects in an OOP language (C# included) keep track of the Class that created them.
If Unity would do the same thing with “clone object1 to create object2” … again, that would make many of the above approaches easier and less error-prone.