Unity Super-Helpful Tips: Scripting

unity-logo

This is the first in a series of (hopefully) helpful mini-tutorials and tidbits of knowledge I’m gonna be passing onto you, my loyal readers! I say series – you know how these things usually turn out, but I’ll do my best! There’s a wealth of hidden tricks that some of you won’t know about – the point of these posts are to reveal them to you all in their globally-illuminated beauty. I swear Unity 5 hasn’t gone to my head. This post will focus on scripting – I’ll be using C# examples mostly, but these tips should also be applicable to Unityscript.

Changes from Unity 4 to 5

First up, only a tiny change, but an important one; you know all those helpful property getters such as xxxxx.rigidbody, xxxxx.renderer, etc? They got killed off in Unity 5. The API changed, and to better modularise the codebase, these properties no longer exist. A moment of silence for our fallen friends, please. With the exception of .transform, which stays because it’s part of Unity’s core module and not the physics or rendering modules, you’ll need to use something like:

myComponent = GetComponent<MyComponentType>();
myTransform = this.transform;     // Still fine.
myGameObject = this.gameObject;   // Fine, part of MonoBehaviour.

Don’t worry though – Unity 5 ships with a tool for automagically finding usage of the deprecated API and switching usages of it for the new one, so switching old projects to shiny new Unity 5 goodness is a breeze. 

Attributes: Serializable and SerializeField

These are two very helpful attributes I use a lot – SerializeField can be used to make any variable appear in the Inspector, even if it is private.

private int myInt = 0;
[SerializeField] private float myFloat = 0.0f;

The first variable, myInt, won’t show up in the Inspector, as it is private. However, myFloat has the SerializeField attribute before it, so it’ll defy its private modifier like a moody teenager and show up in the Inspector anyway – super handy if you have variables you want to change quickly AND keep private.

Serializable is useful for taking entire private classes and making them visible to the Inspector. But when would that be useful? Say you had a particular kind of object that makes it easier to hold a set of values, but then you only need those objects in a different class.

using UnityEngine;
using System.Collections;
using System;

public class MyClass {
    [SerializeField] private DataHolder dataHolder;
    // do other stuff in the class like usual...

    [Serializable]
    private class DataHolder {
        // variables, methods like usual...
        // can only be accessed from MyClass.
    }
}

In the code, no external class can access the DataHolder class, but since we have added the Serializable attribute, we’ll be able to access any public variables stored in DataHolder in the Inspector through MyClass’ dataHolder variable. It’s also worth noting you’ll be able to see any of DataHolder’s private variables if you prefix them with a SerializeField, too, and you can have arrays and lists of DataHolders. Since Serializable is contained in the System namespace, you’ll need to put “using System;” at the top of your code, too.

Preprocessor Directives – different code based on export platform

One of the great things about Unity is its portability – it exports to EVERYTHING. But what if you wanted one script to do different things based on the platform it’s on? A great example would be controls, which are completely different on mobile compared to desktop. There’s a way to do this using preprocessor directives. Don’t let those huge words dissuade you, they’re easy as hell to put in and very useful!

#if UNITY_EDITOR
    Debug.Log("Only runs in the editor");
#elseif (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8)
    Debug.Log("Only runs on mobile");
#endif

With these, you can make different parts of the same script become invisible to the platforms they’re not needed for. There’s a whole host of Unity-specific directives, and (in C# at least, I’m not sure about Unityscript) there are many more ways to utilise them.

Invocations and Coroutines

Both of these are hugely useful, and you ought to be using them all over the place. Invoking a method is basically like telling Unity “Do this please. But not yet – maybe in five seconds”. You can even go as far to say “Well, do this thing in seven seconds. Then every three seconds after that” using InvokeRepeating.

Invoke("MyMethod", 5f);
InvokeRepeating("MyRepeatingMethod", 7f, 3f);

Coroutines are a bit more of a pain in the ass to get working, unfortunately. It’s a shame, because they’re awesome. Coroutines are a special kind of method that allow you to use yield statements – in non-jargon terms, you can tell a method to hold its horses and wait for a few seconds, or until the end of the current frame, amongst other things. There’s the CancelInvoke() method too, which will remove all invoked instances of the method passed in and stop them being executed after their waiting time.

void Start () {
    StartCoroutine("DoStuff");
}
private IEnumerator DoStuff () {
    Debug.Log("I'm gonna do stuff");
    yield return WaitForSeconds(2f);
    Debug.Log("I did stuff! Woo!");
}

It’s worth noting a few things: in C#, a coroutine must be called with the StartCoroutine() method. You can pass in the method name as a string (plus one parameter after a comma), or like this

StartCoroutine(DoMoreStuff(5f, 7f, 9f));

which is far more useful for any method that requires more than one parameter. The coroutine method itself must have a return type of “IEnumerator”, and will break horribly if it doesn’t return any yield statement. The “WaitForEndOfFrame()” yield type is really useful because it lets you call code after LateUpdate() has finished doing stuff – consult the order of function execution diagram for more information. Seriously, that thing’s basically the equivalent of the 10 Commandments for Unity Developers. There’s also a StopCoroutine() method which is effectively the analogue of CancelInvoke() but for coroutines.

I’ll be back soon with a heap of useful Unity things soon – it probably won’t be scripting next time, but we’ll be digging into the engine itself.

Advertisements