futurice/unity-good-practices

Name: unity-good-practices

Owner: Futurice

Description: Good ideas for Unity3D development, by Futurice developers. http://www.futurice.com

Created: 2017-12-27 11:32:58.0

Updated: 2018-05-23 08:36:50.0

Pushed: 2018-04-24 07:50:43.0

Homepage: null

Size: 26

Language: null

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Unity3D Good Practices

Work in progress, please contribute or fix things as you see fit

Interested in other platforms? Our Best Practices in Android Development, Windows App Development Best Practices and iOS Good Practices documents have got you covered.

Contents

If you are looking for something specific, you can jump right into the relevant section from here.

  1. Tutorials
  2. Recommended assets
  3. Coding conventions
  4. Optimizing
  5. Testing
  6. Tools
Tutorials

Here's a list of recommended tutorials for getting to know Unity.

Recommended assets

This section will list some highly recommended but perhaps not always relevant assets. Not mentioned in this section is Reactive Extensions for Unity which we will cover later. To get an overview of a lot of open-source assets, check out AssetLoad

DOTween - Animating from scripts

You could be spending hours upon hours writing your own easing functions in some Update-loop or UniRx Observable - or you could use a tweening library. We recommend using DOTween. It's easy to learn, has a lot of functionality and it's faster than the others! This is extremely handy when you're trying to implement some simple UI animation for example. Do not use this for organic animation, e.g. characters. Refer to Unity's built-in animation system in that case.

Here's a quick example on how to move something to a new position over two seconds with a quint easing, and finally logging to the console:

sform.DOMove(new Vector3(1,2,3), 1).SetEase(Ease.InOutQuint).OnComplete(() =>

Debug.Log("Hello world");

TextMesh Pro - Better text for your games

Ever think 'The text in Unity is so bad…'? Worry no more, TextMesh Pro is here to save you.

There's not much to say here, it's “just” text, done right. You can use text as a mesh in your 3D world, or use it as text for your canvas.

Coding conventions

In this section we'll talk about raising the quality of your code. Some of these tips may affect performance, so be mindful.

C# version

Unity builds to a runtime version of dotnet 3.5 by default. You can switch to 4.6 by going into Player -> PC, Mac and Linux -> Other Settings -> Configuration -> Scripting Runtime Version. Here's an example of a getter property in 3.5:

ate int _foo;
ic int Foo

get
{
    return _foo;
}

And here's the same in 4.6:

ate int _foo;
ic int Foo => _foo;

For more information, see here.

Code style

To keep your code maintainable and readable by yourself and others as well, it's recommended to determine a specific style for your scripts. It's up to you to determine what's best for yourself and your teammates. Here's one way to do it:

space MyNamespace

public class MyClass : MonoBehaviour
{
    // private fields start with underscore and then lowercase
    // public fields start with no underscore and uppercase
    // variables start with no underscore and lowercase
    [SerializeField] private float _myPrivateEditorField = 1f; // starts with default value of 1, gets overriden in editor
    [HideInInspector] public float MyPublicHiddenField = 2f; // can't be seen in inspector, but other scripts need this variable
    private float _myPrivateField = 3f; // cannot be seen in inspector
    public float MyPublicField = 4f; // is automatically serialized and can be seen in the inspector

    public float MyPrivateFieldAccessor => _myPrivateField; // other scripts can now read _myPrivateField

    public void MyFunction(float myParameter)
    {
        var myVariable = 5f;
    }
}

LINQ

Language-integrated query, or LINQ for short, is a way of handling collections in dotnet.

Let's say you want to find a gameobject in a list that has some specific tag:

Object gameObjectToFind = null;
ach(var go in yourListOfGameObjects)

if(go.tag == "fooBar")
{
    gameObjectToFind = go;
    break;
}

We can do this much shorter with LINQ:

gameObjectToFind = yourListOfGameObjects.FirstOrDefault(go => go.tag == "fooBar");

As with loops, you should be careful of using these sort of statements in frequently run scopes like Update or FixedUpdate. LINQ in particular allocates more memory than a trivial for-loop. Internally, LINQ still uses for-loops - but your code will be easier to read, at least once you get to know LINQ. See more examples of LINQ in Unity here

Coroutines vs reactive extensions

By default, Unity offers Coroutines for handling future execution. In this section we'll go through the cons of that, and introduce an alternative featuring reactive programming.

Here's an example of a timed execution in Unity:

 Start()

StartCoroutine(DoOnDelay(5f));


merator DoOnDelay(float afterTime)

yield return new WaitForSeconds(afterTime);
// stuff that happens after time

There's a couple of problems about this. Why are we dealing with IEnumerators? What's with all this boilerplate for a simple timer? There are better and easier ways to do this. Don't do this either though:

 timerStarted = false;
 doExecuted = false;
t dt = 0;

 Start()

timerStarted = true;


 Update()

if(timerStarted)
{
    dt += Time.deltaTime;
    if(dt >= 5f && !doExecuted)
    {
        doExecuted = true;
        // stuff that happens after time
    }
}

This is even more boilerplate. We have to try something much more shorthand. We recommend Reactive Extensions For Unity, UniRx for short. You can download UniRx from the Asset Store

Here's the same example as above, but written with UniRx:

rvable.Timer(TimeSpan.FromSeconds(5f)).Subscribe(_ =>

// stuff that happens after time

UniRx is based on the reactive programming paradigm. In this case, we start a timer that lasts five seconds and we subscribe to that timer stream. Subscription means that we're listening to when the timer is done. In reactive programming, whenever you do not wish to use the value handed to you from the publisher (here, the timer), you name the input parameter of your lambda expression as underscore.

You can also create oscillator-type timers that trigger every X TimeSpans by supplying a second parameter in timer like so:

rvable.Timer(TimeSpan.FromSeconds(5f), TimeSpan.FromSeconds(5f)).Subscribe(_ =>

// stuff that happens after every time

Please refer to the UniRx github page for more examples.

Optimizing

If your game is getting laggy, try reading some tips below.

Object pooling

If you're instantiating/destroying a lot of objects, try using an object pool. The principle is simple

This will make life much easier for your CPU. See a detailed tutorial here and here

Profiling

Coming soon…

Testing
Unit tests

Coming soon…

Platform dependent compilation

Switching platforms for sections of your scripts is done like so:

 Start()

#if UNITY_EDITOR
Debug.Log("Hello Unity Edtor");
#else
Debug.Log("Hello something else");
#endif

This is great if you want to skip parts in your application/game. See more here.

Tools
IDEs

This is a matter of taste, but other than the built-in MonoDevelop, you can try out Rider, Visual Studio or Visual Studio Code. There's a lot of different IDEs and different arguments for which one is the best.

Personal experiences: Visual Studio Code does not work very well for debugging


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.