Beginner Unity Script Troubleshooting
Between mentoring game jams and working on my own stuff, I’ve ended up with a list of basic Unity scripting pitfalls, many of which don’t cause runtime exceptions or log any errors. I emphasise that most of these are really basic, and anyone who spends a while with the platform will internalise them sooner or later, but if they’re not yet obvious to you, I hope you find the list useful.
Note that I’m a game designer, not a software engineer. If you know this stuff better than me and you find anything that isn’t best practice, point it out so that I’ll know better next time 🙂
It Doesn’t Run
You played the game and didn’t see the expected results, so you stuck in some Debug.Log()s and they didn’t fire. (Yes, I realise that this isn’t very nuanced debugging. It can still be a lot faster than getting VS to connect to Unity in order to use its debugger.)
- Is the monobehaviour attached to something? It’s easy to forget that part.
- Is the monobehaviour enabled? Inspect the gameobject: next to the component’s name is a tickbox. This appears to be an editor-only option; I can’t find a script method for enabling/disabling a component at runtime.
- Is the gameobject enabled? Check in the editor (tick box next to its name), but also bear in mind that gameobjects can be disabled at runtime (using gameObject.SetActive(false) in current code or directly setting gameObject.active in legacy code). If necessary, play the game in-editor, pause it when required and check the inspector then.
- Coroutines cause particular fun with disabled gameobjects. If you attempt to start a coroutine on a disabled object you get a helpful runtime message. If you disable an object its active coroutines stop, and you don’t get the equivalent message when they were next to run.
- Have you got the Monobehaviour method name right? Because behaviours are ‘messages’ rather than overrides, the IDE won’t notice if you get their names wrong. In VS with the Tools for Unity plugin you can hit Ctrl+Shift+q for a search box to insert boilerplate; if in doubt go look up the available messages. This problem includes several cases:
- Is it called what you thought? e.g. Some are past tense (OnBecameVisible) and others present (OnCollisionEnter).
- Are you calling the 2D version? Because the physics engines are separate, several messages are available in distinct 3D versions (with normal name) and 2D ones (with 2D appended). Generally if you choose the wrong one it won’t be called (I don’t know if you can enable both engines, but I’m sure you shouldn’t).
- Have you mis-capitalised it? They’re in normal C method style, camel case, initial cap. Watch out for OnGUI (both because of the acronym capitals, and because since the new UI system in 4.6 you probably don’t want to call OnGUI).
- Are you sure you’ve got the right event? Often this will cause code to run at the wrong time rather than skip it entirely, but it’s worth checking the manual entry for the event you’ve used. Commonly confused messages include Start and Awake, Trigger/Collider Enter vs. Stay, and so on.
- Are the conditions as you expect? Is a conditional preventing the code you want? If you’re silently returning on null, the problem may be with a null object rather than the code at hand (P.S. Don’t silently return on failed checks; log something or throw an exception). This may be best examined with a full debugger, but it might still be quicker to shove in some more Debug.Log and play it again.
- If it’s a Collision/Trigger event, do you meet the requirements? Roughly speaking, in order to get a collision, you need two colliders and at least one of them must also have a non-kinematic rigidbody. In order to get a trigger event, you need a trigger (with the script), a collider or trigger on the other body, and at least one of them must also have a rigidbody (but it can be kinematic). Colliders are a little complicated (especially when you start to compound them and use them with rigidbodies): RTM.
It Doesn’t Find What It Needs
I’ve been using Unity on and off since probably version 2 or 3, and I’m sure there used to be more ways of finding things. But never mind.
Find objects with the various Find* methods of GameObject (or inherited from Object); find components with the GetComponent* methods of GameObject or Component. Note that:
- Many Find* methods will find only active gameobjects. This is subtle on their manual pages, which often just sneak that word into the initial description.
- ‘Find’ itself doesn’t seem to say that, so it might find inactives. But I wouldn’t count on it. It’s also probably the most expensive of the lot.
- GetComponent and its variants also only search on active components.
If you’re having trouble finding an inactive object, try finding it while it’s active and assigning it to a variable in the script that will later need it. (Note that if you put it into the scene active to find it but still want it to go inactive, having it found during Awake and hidden in Start). In fact, do this anyway to avoid calling Find methods too frequently, as described in the manual page for Find.
Other finding problems include:
- Did you definitely put the gameobject into the scene?
- And put the desired component onto it?
- Programmatically, you say?
- Check it exists. Play in editor, pause, check that the object you’re trying to find has actually been instantiated (and that it has whatever name, tag or component you’re searching for).
- Don’t leave your clones called ‘prefabName(clone)’. Set gameObject.name to change them, ideally to something that’ll be unique per object.
- Rather than searching for an instantiated object (or an added component) later, try storing the reference while it’s still in the instantiating scope. For example, your grenades may be instantiated when the player throws them, in which case you could store a reference on the player (or on anything else the player has in scope). If grenades need a lot of tracking that may be an argument for a GrenadeFactory object.
- Check it exists. Play in editor, pause, check that the object you’re trying to find has actually been instantiated (and that it has whatever name, tag or component you’re searching for).
If you’re trying to instantiate various interrelated objects and components and their references aren’t connecting up, you may have trouble with execution order. The most Unity thing to do is probably to put more things into the scene in the editor, rely less on instantiation, and so on, but I like procgen and I like to have the design saved in in text rather than the editor. Here are a few tips for this approach:
- Read up on execution order: If you’ve got clashes during Start(), try moving the more fundamental ones to Awake(), which runs beforehand.
- If you still don’t have enough control over things being ready, stop using Start() and Awake(), make an Init() or similar and call that when you know you’re ready, as you would with the constructor on a non-monobehaviour. (In fact, sometimes you don’t need things to be monobehaviours, and could have another component own them.) It’s another thing that could go wrong (effectively adding ‘Did you initialise it?’ to the checklists above) but now you can trace the path of execution and control its order.
- I also use these Init methods to pass in data the object will need. I don’t know whether there’s a ‘more Unity’ way of doing this; if there is I’d love to hear it.
- If two objects appear to have circular dependency (each needs the other to be ready), take the more complicated and divide its initialisation into two steps, the ones that don’t require the other object, then the ones that do. This generally doesn’t arise with monobehaviours, since they can already have pre-initialised existence sufficient to get a reference.
If you’d like any examples, have questions, or can correct/improve anything, tell me!