General Tips

This page contains random notes and tips to myself about my experience using Unity game engine. This page will always be updated with more items as I think of them. Once there is enough items to require it, I will categorize the tips.

The given code examples are not tested and added for demonstration purposes only unless specified otherwise.

  • Read the Microsoft Performance recommendations for Unity

  • Read the ARM Guide for Unity Developers Optimizing Mobile Gaming Graphics

  • Don’t put the scripts that the rest of the codebase need to access on UI elements (In Unity UI, NGUI etc.). UI elements are disabled/enabled all the time along with their parent panels, which makes those scripts unusable. Prefer to keep those scripts separated from the UI and keep a reference to the UI elements they operate on in them.

  • When you don’t know how a Unity API method/property works, decompile the Unity dll that contains the member and take a peek, chances are it’s visible if it’s not an internal engine call. Modern IDE’s usually have this feature (go to implementation)

  • Change the color of the Unity in Play mode to a darker shade in the preferences, sometimes it’s hard to distinguish the difference in the default color scheme and it sucks to lose your inspector changes because they are not preserved when you exit the play mode.

  • If you change some values in the inspector in the Play mode, if you wish to preserve these settings, copy component from it’s context menu, exit the play mode, paste component values back.

  • Don’t rely on Asset Store assets. Doesn’t matter how frequently they are updated or how active the developer is. It’s always possible that Unity will acquire the asset and integrate it into the engine and remove it from the asset store. Or the developer may just vanish. (BetterTrails, Shaderforge, Haste and many others are discontinued Procore bundle acquired by Unity and removed from the Asset Store forcing people to update Unity to continue using these tools not to mention these tools were unavailable for a couple of months until the new Unity version was out. There are possibly many more examples of this). You may say “It’s source is open I can continue developing it to my needs and update it for new Unity versions when necessary” but it’s just a new burden on your back and it’s no fun fixing third party codebases while trying to make a game.

  • NGUI: Don’t put position tweens on anchored widgets, put the widgets with position tweens under anchored containers. Otherwise the anchor will just override the position changes and your object will stay still.

  • In a method if failure is not tolerated use exceptions, they will stop the execution of your game and let you see what gone wrong where.
    Otherwise handle the problems where you invoke the method. If the method is of type void, change it to bool and return a success status flag. If it’s not void, but returns a reference type return early with a null, if it’s a value type just make it a nullable type via the shorthand ? operator like int?. If you don’t want to return null, return an error value that you know like if only acceptable values are unsigned, return -1 and check for that when you invoke the method.
    This way you can check the return type and handle the failure and print a pretty log about what’s wrong and continue the game execution.

// Failure is tolerated
public int FindEnemyTypeCount(EnemyTypes type) {
	if(_enemies == null) {
		Debug.LogErrorFormat(this, "Trying to find {0} enemies but the _enemies list is null!, returning 0", type);
		return 0;
	}

	int count = 0;

	for(var i = 0; i < _enemies.Count; i++) {
		if(_enemies[i] == null) {
			Debug.LogWarningFormat(this, "There is a null enemy at index {0}", i);
			continue;
		}
		if(_enemies[i].type == type) count++;
	}

	return count;
}

// Failure not tolerated
public int FindEnemyTypeCount(EnemyTypes type) {
	if(_enemies == null) {
		Debug.LogErrorFormat(this, "Trying to find {0} enemies but the _enemies list is null!", type);
		throw new NullReferenceException();
	}

	int count = 0;

	for(var i = 0; i < _enemies.Count; i++) {
		if(_enemies[i] == null) {
			Debug.LogWarningFormat(this, "There is a null enemy at index {0}", i);
			throw new NullReferenceException();
		}
		if(_enemies[i].type == type) count++;
	}

	return count;
}

  • Keep reused coroutines in fields of type IEnumerator. This way you guarantee that only one instance of the coroutine is running at any given time. You can also stop any running coroutines using the variable that stores them instead of the disgusting StopCoroutine(string)
private IEnumerator myCoroutine;

/// <summary>
/// Call this to make sure only one instance of <c>SomeCoroutine</c> is running
/// Stops <c>SomeCoroutine</c> if it's running and starts a new <c>SomeCoroutine</c>
/// </summary>
public void StopStartSomeCoroutine() {
	if(myCoroutine != null) StopCoroutine(myCoroutine);
	myCoroutine = SomeCoroutine();
	StartCoroutine(myCoroutine);
}

private IEnumerator SomeCoroutine() {
	yield return new WaitForSeconds(5.0f);
	Debug.Log("I'm a useless coroutine that waits for 5 seconds before printing this")
}
  • Use a string variable named “name” in your custom classes to be able to rename your array elements of that custom class. Unity uses this for the UI labels for each element and if given an empty string, it will show "Element " + IndexOf(element)

Texture Import Settings dialog

See the next tip for a code example.

  • Use MonoBehavior’s OnValidate method to rename the elements of an array of custom classes. Consider the following code
public class SomeComponent : MonoBehaviour {
	public SomeCustomClass[] TestArray;

	/// <summary>
	/// Invoked every time this MonoBehaviour is attached or any value in it's inspector is changed
	/// </summary>
	private void OnValidate() {
		// Validate every element of the array every time the MonoBehaviour is validated
		for(var i = 0; i < TestArray.Length; i++)  TestArray[i].Validate();
	}

	/// <summary>
	/// Nested custom class for demo purposes
	/// </summary>
	[System.Serializable]
	public class SomeCustomClass {

		/// <summary>
		/// Variable to name elements of an array of type SomeCustomClass
		/// This variable must be named "name", NOT case-sensitive
		/// In this specific example it's hidden in the inspector because OnValidate will name the elements using other fields in this class
		/// </summary>
		[HideInInspector] public string Name;

		public enum Currencies { USD, EUR }

		public Currencies Currency;
		public int Price;

		/// <summary>
		/// Changes the <c>Name</c> field to the form Price Currency 
		/// <example>500 USD</example>
		/// </summary>
		public void Validate() {
			Name = string.Format("{0} {1}", Price, Currency);		
		}
	}
}
  • Use the ContextMenu attribute for quick testing within the Unity editor without running the game. The name argument supplied to the ContextMenu attribute will appear in the containing MonoBehaviour’s context menu
public Transform[] Transforms;
public float radius;

[ContextMenu("Randomize Positions")]
private void Test() {
	RandomizePositions();
}

private void RandomizePositions() {
	for(var i = 0; i < Transform.Length; i++) {
		var t = Transforms[i];,
		if(t != null) t.position = insideUnitSphere * radius;
	}
}