18 tips for working with Unity that I learnt whilst developing Monstapals

, a 20-minute piece by Brad Mclain Brad Mclain

Anomaly recent released Monstapals our first gaming title which is targeted at kids aged 4 and up. It was which was built using Unity and whilst I already had experience using Unity in the past I learnt a lot during the project.

Here are 18 things I learned that I hope others might find useful as well.

1. Setup Smart Merge for your version control system

If you are using version control with Unity (which you really should be) there will come a time when you need to merge a scene or prefab and the standard merge tool fails you. To help with this Unity has provided us with SmartMerge.

If you are using something like Perforce or PlasticSCM then it is as simple as enabling the option under Edit > Project Settings > Editor, however if you are using something like Git then the process is a little more complicated.

Add the following to your .gitconfig substituting VERSION with your Unity version:

tool = unityyamlmerge

[mergetool "unityyamlmerge"]
trustExitCode = false
cmd = /Applications/Unity/Hub/Editor/VERSION/Unity.app/Contents/Tools/UnityYAMLMerge merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

Just remember to keep this up to date when you update Unity if you are using Unity Hub.

2. Use the Unity Cache Server

One of the worst parts about about Unity is waiting for assets to import. There is not much you can do about this the first time you add an asset or run a project but fortunately the Cache Server exists to help make this process quicker.

If you are working by yourself this can just be enabled under Unity > Preferences and selecting Local under Cache Server. However if you are working in a team or across multiple machines you’ll want to use the Remote option and check out the standalone version available on GitHub.

Whilst the Cache Server can support multiple projects the Unity team recommends running a different server per project to be safe.

3. Checkout your project for each platform rather than platform switching

Even when utilising the Cache Server switching platforms can often take a frustratingly long amount of time. If you have the spare disk space I found it better to have a different checkout of the project for each platform rather than using platform switching.

This is particularly useful for Sprite Atlases which are currently not supported by the Cache Server.

4. Don’t mix 2D and 3D Physics

This is definitely more of a thing that caught me out once that I wanted to warn others about. Unity has two separate physics systems depending on if you are working in 3D or just 2D. These each have their own distinct scripting packages Physics and Physics2D.

So if you are working in just a 2D project make sure all the colliders and rigid bodies you use are the 2D variants as well as all calls to the physics namespace in your scripts being Physics2D. This all seems pretty straight forward and make sense but just make sure your IDE autocomplete doesn’t pick the wrong option for your use case leaving you wondering why a particular object or layer is not responding to physics.

5. Avoid slow operations in the Awake, OnEnable and Start methods to prevent the game freezing during scene load

When loading a new scene you’ll often want to do it in the background or show some kind of progress bar. To do this you will need to make use of SceneManager.LoadSceneAsync.

Scene loading in Unity takes place in two stages: asset loading and scene activation. By default scene activation happens automatically once asset loading has completed. This can be changed by setting AsyncOperation.allowSceneActivation to false. If you are looking at the AsyncOperation.progress value then 0.0 to 0.9 represents asset loading whilst scene activation occurs in a single update cycle and will jump from 0.9 to 1.0 when done.

During scene activation the Awake, OnEnable and Start methods will be called for all objects in the scene. Since this happens in a single update cycle if you have any slow operations in any of these methods your game can often freeze whilst this is processing.

Making use of something like Coroutines won’t help you here because as soon as you return from something like Start Unity will consider it completed which would be bad if anything in your start method has to be completed before the scene is shown to the player. The best way to work around this is to have another object in the scene which is responsible for making sure these operations are completed and your scene loading code can refer to this to see when the scene is ready to display to the player.

One thing to note here is that if you have loaded a new scene using the Additive method it does not automatically become the active scene. Which leads to…

6. New objects are always instantiated in the active scene

Any new objects you instantiate will always be created in the active scene. As mentioned above if you have been loading scenes using the Additive method you will need to make sure to set the active scene before instantiating to ensure they end up in the desired scene.

This can be done by using SceneManager.SetActiveScene.

7. Make sure you stop listening to events when objects are destroyed or disabled

Setting up an event system is often a great idea for having disparate parts of your game respond to certain events without needing to pass references around for everything.

However leaving one of these still listening after the object has been destroyed or disabled can often be a source of bugs and confusion. The best pattern for ensuring these are cleaned up is adding them in OnEnable and removing in OnDisable.

void OnEnable()
    EventManager.OnSpaceshipEntersSky += EventManager_OnSpaceshipEntersSky;

void OnDisable()
    EventManager.OnSpaceshipEntersSky -= EventManager_OnSpaceshipEntersSky;

A good friend of mine (who is definitely not me) reminds you to just make sure you change the += sign if you copy paste.

8. Always move assets in unity editor not in file system or IDE

Anyone who is comfortable with working in their favourite IDE or knows their way around the command line will often prefer these tools for renaming or moving files. In Unity however this is a bad idea.

Moving a file around using these methods will break any references you have setup between objects. Dragging them around in the Unity editor however preserves these links making your life a lot easier.

9. Make use of the inspector “lock”

The inspector panel shows details about the currently selected object from the project browser. However sometimes you want to be able to browse or search for objects in the project panel and ensure the current item stays in the inspector. This is what the lock button is for.

Unity inspector lock

I found this quite handy when adding folders or sprites to sprite atlases or comparing objects to one another.

10. Use full namespaces when referencing third party packages

Using third party packages or plugins from the asset store or other sources is a great way to speed up your development and avoid re-inventing the wheel. However I discovered a slight gotcha when working with third party packages.

The code snippet below:

using UnityEngine;
using ThirdParty

class MyBehaviour : MonoBehaviour {

    void Start()
        GestureRecognizer tapGesture = new GestureRecognizer();


is better written as:

using UnityEngine;

class MyBehaviour : MonoBehaviour {

    void Start()
        ThirdParty.GestureRecognizer tapGesture = new ThirdParty.GestureRecognizer();

The reason for this is that in the first example there is a chance that during a Unity upgrade the API updater will recognize GestureRecognizer as the Unity class in UnityEngine.XR.WSA.Input and attempt to change it.

This advice can also apply to referencing your own code.

11. Default render texture depth

The default RenderTexture as created in the editor only has a depth of 16 and no stencil buffer. If you are using masks will need to use a depth of at least 24 to get a stencil buffer.

To achieve this you can create one in your scripts:

RenderTexture renderTexture = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);

12. Use Image instead of RawImage when sprites are involved

RawImage is a general purpose component for image display that can handle any texture whilst Image is just for sprites.

The advantage to using Image is that it works with sprite atlases whilst RawImage will load the texture again even if it is contained in a sprite atlas which is already loaded into memory.

13. Make use of FormerlySerializedAs to rename inspector variables

The longer a project runs the higher the chance that you will need/want to rename some of your inspector variables. Maybe your terminology within the project changed and the code should be updated to reflect that or possibly it was just named wrong in the first place.

using UnityEngine;

public class MyBehaviour : MonoBehaviour {
    public float speed;

If you were to simply go ahead and change the name of the inspector variable then the contents of it will be lost. This is where FormerlySerializedAs comes to the rescue.

using UnityEngine;
using UnityEngine.Serialization;

public class MyBehaviour : MonoBehaviour {
    public float velocity;

Usually it is safe to remove this once Unity has been run once and the data has been serialized with the new name.

14. Inspector references will always be loaded into memory

I originally thought that inspector variables to assets were just references that would be lazily loaded when used. This not true, anything referenced by a script in the inspector will be loaded into memory whether it’s used or not. This can cause lots of unnecessary memory usage especially if lots of game objects link to one another.

To have assets only loaded when required at runtime you will need to look at either Resources.Load or AssetBundle.LoadAsset depending on if you are using Resources or Asset Bundles.

15. Make use of Animator Override Controllers

If you have a lot of similar characters in game who share the same animator logic but have different animations you will want to make use of Animator Override Controller.

This allows to you specify a base Animator Controller and replace just the animations whilst keeping everything else the same. The makes future maintenance of these characters much quicker and less error prone allowing you to make changes in one place.

16. Search your assets and save your favourites

A very handy feature of the Project tab is that you can search your assets by type using t:. This is particularly useful for finding something like all sprite atlases if you want to change their quality or compression format at the same time. Unity asset search

You can then use the star button to save your favourites for future use. Unity saved searches

17. Make use of development builds and attach the profiler

Game using more memory then it should or running slow a certain device? If you run the profiler in the Editor you can’t trust any of the given numbers because it contains the overheads of running the Editor as well as all sorts of extra loaded assets that won’t exist in a player build.

Unity development build

To solve this you’ll want to do a build with the Development Build flag enabled and then attach the profiler to the device running the development build.

18. Have you tried turning it off and on again?

Sometimes you’ve double and triple checked something that you are certain should be working and still have no idea what is going wrong. If you run into this scenario it can be worth restarting Unity to see if this will magically fix the problem.

Download Monstapals today

Announcing Monstapals

Download Monstapals on the App Store or Google Play Store today.

Made with Unity

Next Up: a 1-minute piece by Dev Mukherjee Dev Mukherjee

Re-launching Safe Sharps

Read more