50 Tips for working in Unity
Source = Dev.Mag
About these tips
These tips are not all applicable to every project.
About these tips
These tips are not all applicable to every project.
- They are based on my experience with projects with small teams from 3 to 20 people.
- There’s is a price for structure, re-usability, clarity, and so on — team size and project size determines whether that price should be paid.
- Many tips are a matter of taste (there may be rivalling but equally good techniques for any tip listed here).
- Some tips may fly in the face of conventional Unity development. For instance, using prefabs for specialisation instead of instances is very non-Unity-like, and the price is quite high (many times more prefabs than without it). Yet I have seen these tips pay off, even if they seem crazy.
Comments
I have a few questions though, at point 30 where you say you mustn't show properties in the inspector if they aren't needed, surely one could use [HideInInspector] or would that be bad for some reason?
Also, something I've often wandered is how would you store...data...about types of objects in a game. For example if you had a game with 20 types of enemies each with their own plethora of stats, how would you go about storing that data? With prefabs? Creating assets of instances of ScriptableObjects? Even then, how would you go about referencing then? Would you just have a list something in a manager and list them all there?
Making variables public is generally bad practice. Making a variable public says to your team mate (or your future-self: "change me! it is safe!" when it may well not be.
In one game I worked on, I could not figure out how to make things die... just because all the variables were public (health, isAlive, and a bunch of others). It took a whole day to figure out how these all related to each other and actually make the actors die (and stay dead...)
As for storing data, I always use prefabs.
The details depends on many things (such as how they differ visually). One way to do it is to have two prefabs for each enemy. One is the art, physics and animation prefab. The other contains just the data. (See tips 17 and 33). This setup makes it easier to tweak game design data versus technical data or visual effects. The first prefab contains a reference to the second, so it is easy to access the data from code.
Of course, when you start getting to 20, you may consider some other alternatives. Spreadsheets can work great (and are easy to read in as well). Using conditional formatting you can also make it very easy to see visually how enemies get stronger, or whether some value is way off.
On private vs public, vars should traditionally only be public if you specifically want them to be accessible outside your class. Unity complicates this by making public the way to expose stuff in the editor as well, but at least you can add [HideInInspector] to fields that should be publicly accessible in code, but designers shouldn't touch in the inspector. Otherwise default property setups are useful but potentially expensive:
public bool Foo { get; set; }
On storing large amounts of data, such as text and levels, XML is good (and a traditional way to do things) but we've found JSON actually works out better in some regards. It's less verbose, so less wasted space (eg in savegames) and because the structure maps objects pretty much exactly it's easier for everyone to understand. It also plays well with web services (more easily than XML) which helps in some things. There are some excellent json serialization/deserialization libs freely available for Unity that work just fine.
On naming, I'm also a fan of Pascal case, and always have been. I also tend to despise underscores. One thing is causing me to lean towards all lowercase with underscores as separators though: Mac vs Windows and their handling of case. In theory sticking to Pascal case is ideal, but there are so many fringe cases where people use different capitalization for things, like Background vs BackGround and things get muddled, and this has led to issues. I dunno, still trying to decide where I fall on this.
On referencing prefabs and never using strings for anything, for the most part I agree, but we found an interesting Unity bug that has led to us to using named prefab paths (aaaargh) instead of prefab references. Unity takes significantly longer to instantiate objects from a prefab reference compared to instantiating it from the prefab path on Android. As in almost an order of magnitude longer. We had epic loading time issues, switched to using named over references, and gained back minutes of load time!
The only reason why I still prefer XML, is because so many tools save in that format, so it's much easier to get something to edit the data.
... hmmm we have not experienced this bug... this is worrying.
It's just one of many tiny optimization choices that probably end up being more pedantic than they're worth :P
I still twitch slightly every time I declare a public variable though... Can you elaborate on this please? I haven't really gotten down to writing GUI's... This makes sense, though I have some questions on what are good practices in this area...
If I understand correctly, you should basically have a blank script in the root prefab that will have references to (typically) 3 other prefabs for MVC - but if I then drag that prefab into the scene the 3 other prefabs will not be instantiated automatically? Or am I misunderstanding? Or are you simply implying that for any prefab should be split into subobjects? Reading it again it seems like the latter is the case.... My concern is that I typically have (as is mentioned in pr0tip 17) two or more very similar prefabs. I guess there is no way getting out of duplicating a lot of work on either, considering that nested prefabs don't work?
8.
Yes, that is worded a bit vaguely. It is basically to prevent this kind of thing:
Parent container arbitrarily placed at (100, -50). Child, meant to be positioned at (10, 10), then placed at (90, 60) [relative to parent].
This error is common when the container is invisible, or does not have a visual representation at all.
19.
I see I actually mean something other than what I wrote... o.0. What I mean is to add the mesh to a node, and then make the entire thing a prefab. Scripts should then be added to the root node. (Common practice is to make the mesh a prefab directly, and add scripts to that).
The reason for this is that it is easy to replace the mesh without loosing any inspector values that may already have been tweaked.
(I'll clarify the original article).
Edit
Hmmm I see 19 is actually three separate points that were mislabeled...grrr that's what happens if you do manual counting :(
-
There is a way to get the same benefits as you would from nested prefabs by linking in prefabs, and instantiating instances on Awake or Start.
For instance, say we have two enemies with the same halo.
Bot enemies, instead of having the halo attached to the enemy prefabs, just have a link to the halo prefab. When the enemies Awake, the halo prefab is spawned and attached to the right node.
Again, thanks for the article! I owe you a beer.
p.s. you have a typo at nr 33:
Adding my own hard-won knowledge to the pool: When creating geometry through code, never create a Mesh and then assign that to a newly created MeshFilter's mesh field. The MeshFilter already has a Mesh and you'll end up leaking a really, really tiny Vertex Buffer Object until the scene changes... Also, you need to manually destroy the MeshFilter's sharedMesh if it's something you've generated on the fly.
Don't think many Unity users will be doing procedural generation stuff though, prefabs are usually easier.