Coding Gotchas for New Unity Developers
Starting to learn anything new is a challenge, Unity is no exception. Here are a few tips to make your journey easier. Ready Developer One?
Name your variables
Seems like a simple one but give your variables a sensible name that conveys what it is.
You might see well respected people use the first naming for the loop. It is really just bad practice stretching way back to when even reducing the length of the name of a variable was important, but that is not the case today. Reduce the cognitive strain by getting into the habit of correctly naming your variables.
Don’t use multiple property calls on a single line
One of the most common mistakes and sources of confusion is to use a number of property/method calls on a single line. E.g.
It is really tempting to just write everything on one line. As you become more experienced you will learn where it is acceptable to take such risks, but be clear it is a very real risk.
The examples above both have points of failure that when they go wrong it will be difficult to know why. Classic examples of an exception from running the above code will be, “null exception on line 1” or “null exception on line 2”. Since we have written a number of requests on each line we cannot know which of the requests is causing the problem.
Don’t write one line if you can write two
The multiples lines have been split out into individual calls. Now if we get our null exception it will tell us the exact line that is causing the problem. E.g. if it says, “null exception line 5” then we know we are trying to access a null object, the object in that line is cubeParent. Therefore looking at the line where that is set we know that our _cubePrefab transform does not have a parent. A couple of points to note here; 1) on line 4, I have just done what I said not to, I’ve used two property calls. At this point in my Unity experience I am confident that it will always have a transform, therefore I can use that knowledge to make the code a little prettier. The lesson is to be honest with yourself, if you really do not understand what is going on, then play safe, write the extra line. 2) “I was told to always null check” — yes I’ll get to that soon, but the important lesson is that whilst you are learning to develop it is VERY important to get good focused exception messages, and to do that you need to do less on each line.
Another reason to not skip writing extra lines is a tricky little subject often referred to as closures.
The code above looks pretty innocent. It has a loop that is constructing two things; 1) array of strings from “Fred 0” to “Fred 9” and an array of Action delegates that will log “Jane 0” to “Jane 9” to the console. Except it won’t work. The last three lines of output you’ll get from the above is;
Fred 7
Jane 10Fred 8
Jane 10Fred 9
Jane 10
To fix this you need to, write another line :) Add this at line 5 and change line 6 to;
var janesName = $"Jane {nameIndex}";
displayNames[nameIndex] = new Action(() => Debug.Log(janesName));
Don’t take shortcuts, unless you really understand what is happening, just keep your lines of code slow and steady and you will have a much easier time of it. In this case, more is definitely better.
Test for nulls
As a general rule of thumb it is a good idea to test for null. In the GetComponent code example we should test the enemy object for null before attempting to access it.
The problem with this approach is what do you do when you have a null? To begin with I would recommend using something like
else
{
Debug.LogError("I was expecting the enemy would not be null");
The above is known as a Run-Time error, i.e. it is something that you will only see once you are running your code. If possible it is better to expose these problems before you run the code, i.e. Compile-Time errors. For example, you will often see the above in Unity’s Start message function. Unity has the ability to enforce relationships via the RequireComponent attribute
But again, when in doubt, write the extra code to check for nulls. If you are at the point were you are having to optimize away your null checks, then you will be very experienced :)
Don’t be afraid to log
This is a very common mistake for people starting out. You have a lot of code but something isn’t working. Don’t be afraid to pepper your code with Debug.Logs…
Once you are happy with some aspect of the code then just comment out the logging (see line 13) to, “keep the noise down”.
NB. Always review you Debug.Logs before building your product. Unlike almost every other .net tooling, Unity does continue to use Debug.Log in your released code and it can be quite expensive.
Be afraid of tight loops
One of my biggest bugbears about Unity is the Editor is very susceptible to getting stuck in our bad code. One easy way to lose a chunk of work is to write what is referred to as a Tight Loop
My recommendation is never have ‘while(true)’ in your code, never, ever, no, nope, nah. Coroutines do help here. If you have something that takes a long time to run then providing you can split the work into a number of small chunks then you can yield control back to Unity — this will prevent the Editor from freezing and you won’t lose your code.
However, even if you do remove the while-true and you are yielding control, be very wary of what code is still running in your loop. Ask yourself how long is that going to take? Will CalculateTheMeaningOfLife return almost instantly so we can yield or will it take a long time? If it’s the latter then you are in trouble again.
NB. If you are about to lose all your work because the Editor is frozen and you have spotted a tight loop, then try this; Edit the looping code by adding
throw new Exception();
If you’re lucky Unity will compile it and run your error and stop.
Magic numbers (and named args)
The last couple of tips have been a little intense, this is a nice easy one to finish with. Never just plug a literal value into a function argument.
“Ah but Paul, I use the latest version of Blah.IDE and it shows me the named arguments”. Yes, that’s true, IDEs can help with the magic numbers from an intent point of view but only if you guarantee that everyone only views your code with that IDE. Is you next job interviewer reading your code on GitHub, they are…oh dear…
The more common ‘magic number’ is where you duplicate a number as a literal. For example, if you know that the 10th child of your model is the characters left hand, then declare it, that way when it changes to be the 11th child you can easily update it without accidentally replacing the wrong 10.
Take Away
- Make your code really obvious
- Avoiding hiding what is happening with multiple calls on a single line
- Avoid While(true)
Above all, take it easy, don’t rush your code. When you’re writing code, if you find yourself saying, “okay I need to access this and then this, and then”, there is no shame in doing each of those steps as a separate line. Choose an easy life.