Article #3 - January 19, 2010
by Scott Mitting (scott@gsdsoftware.com)

synopsis from article index: Between Unity's APIs and features built-in to c#, there are an overwhelming number of possible ways to organizing a project. In this article, we'll discuss a few of the options for organizing inter-object communication, and some of the reasoning and drawbacks behind using each model.

Unity Essentials Weekly

Organizing Object References

Article Table of Contents

The Hidden Agenda for this Article

The goal of this article is to introduce different message passing and method calling mechanisms available in Unity and c#, to provide as a foundation for future articles discussing best practices for designing and organizing your components in your Unity projects. This article presents materials from a variety of skill levels, so don't be too concerned if you don't understand everything from this article after the first read, we will be using this for reference on more in-depth topics down the road.

These future articles will discuss topics ranging from camera selection systems through incorporating components and resources in .Net .dll files for the most solid integrations possible.

The documentation on Unity3d.com also contains a list of ways to communicate between different objects on their Accessing Other Game Objects page in the script reference.

Object Oriented Crash Course

Since Unity developers are immigrating from many different walks of technological life, lets have a quick refresher on some object-oriented programming concepts. If you are not familiar with these concepts, you will want to learn this before continuing with Unity scripting.

CodeProject.com has an in-depth resource for learning object oriented concepts at http://www.codeproject.com/KB/architecture/OOP_Concepts_and_manymore.aspx.

OOP Principles

Some concepts that you should be familiar with from the above link to save yourself a lot of redesign time if you will be designing objects:

You can get away with developing for Unity without these concepts, but a goal of this blog is to help you program for Unity well. One common practice for the uninitiated OOP developer is to use classes like packages, mimicking procedural style programming. I once saw this best described as a paradigm named "Procedural Object Oriented Programming", or shortened to an acronym, "POOP".

If you really want to dig into these concepts above, check out www.objectmentor.com (totally optional)

Static Properties and Methods

Here's a list of some of the differences between static and instance members, and how they are implemented in c#.

Class Properties
OOP Term .Net Keyword Details
Class Methods static Method call within class without instance. No access to "this" reference. Accessed through call to ClassName.Method().
Instance Methods - Method call to an instance of an object. Data is obtained through "this" reference. Accessed through call to instance.Method(), where instance is obtained through instance = new ClassName();
static Property shared by all instances of an object, i.e. this is only one copy of this value for the class. Accessed through ClassName.propertyName.
Instance Properties - Property for one instance of an object. Access through call to instance.propertyName, where instance is obtained through instance = new ClassName();
Figure 1 Class vs Instance Object Members

Below is an example class containing methods and properties of both the class and instance varieties.

source code view: formated | plain text
       
   1:  class InstanceVsClass
   2:  {
   3:      // I am an instance variable, with a unique value for each 
   4:      // instance of this object.  I can only be accessed after
   5:      // a call to "new InstanceVsClass()"
   6:      string instanceVariable = "instance";
   7:      
   8:      // I am a class variable.  I am available without an object
   9:      // instance, and am shared with all instances.
  10:      static string classVariable = "class";
  11:   
  12:      // I have access to both instance and class variables
  13:      string InstanceMethod()
  14:      {
  15:          return this.instanceVariable + ":" 
  16:              + InstanceVsClass.classVariable;
  17:      }
  18:      
  19:      // I don't have access to instance members because I don't
  20:      // have a "this" reference
  21:      static string classMethod()
  22:      {
  23:          return InstanceVsClass.classVariable;
  24:      }
  25:  }
       
Code Example 1 Comparison of Class and Instance Members

Organizing Object References

There are several ways to link up components to communicate with each other, including:

Singleton Patterns

Singleton patterns may be a term that many have seen but aren't completely sure on the definition. Singletons are a shared instance of an object, usually saved to a static property, that is used instead of a series of static methods. The main reason for using a singleton is to allow the object to be instantiated in other ways down the road if needed.

source code view: formated | plain text
       
   1:   
   2:  /// <summary>
   3:  /// Example singleton object in c#.
   4:  /// </summary>
   5:  public class SingletonExample
   6:  {
   7:      /// <summary>
   8:      /// Static class constructor is called first time the class is
   9:      /// accessed.  We are creating our Singleton here.
  10:      /// </summary>
  11:      static SingletonExample()
  12:      {
  13:          singleton = new SingletonExample();
  14:      }
  15:      
  16:      /// <summary>
  17:      /// Reference to the singleton instance
  18:      /// </summary>
  19:      public static SingletonExample singleton;
  20:      
  21:      /// <summary>
  22:      /// Instance value
  23:      /// </summary>
  24:      public string myValue = "test";
  25:  }
  26:   
  27:  /// <summary>
  28:  /// Object using singleton example.
  29:  /// </summary>
  30:  public class Tester
  31:  {
  32:      /// <summary>
  33:      /// Prints the myValue property from the singleton to the console.
  34:      /// </summary>
  35:      public static void Test()
  36:      {
  37:          System.Console.WriteLine(SingletonExample.singleton.myValue);
  38:      }
  39:  }
       
Code Example 2a C# example of using a singleton pattern

There is a performance hit to using singletons over using static methods and properties, very similar to the performance hit for using c++ instead of c. Each call has an extra reference lookup before calling the method, since accessing the singleton is through a static property.

The above example is not well-suited for Unity, because typically you will want to use a singleton pattern on a MonoBehaviour, which cannot be instantiated directly (i.e. you can't call new MonoBehaviour). In order to use singletons with MonoBehaviour objects, you need to assign the singleton during the Awake() or Start() events, Awake() being preferred since its called earlier.

Below is an example usage of a class that GSD Software uses for the singleton pattern for Unity development, which is included as a download for this article.

source code view: formated | plain text
       
   1:      
   2:  using UnityEngine;
   3:   
   4:  /// <summary>
   5:  ///    Example script using the Singleton<T> generic class.  This object
   6:  /// rotates a camera around an object.
   7:  /// </summary>
   8:  public class MyScript : MonoBehaviour
   9:  {
  10:      #region Properties
  11:      
  12:      /// <summary>
  13:      /// Camera being moved
  14:      /// </summary>
  15:      public Camera targetCamera;
  16:      
  17:      /// <summary>
  18:      /// Object camera is moving around
  19:      /// </summary>
  20:      public Transform target;
  21:      
  22:      /// <summary>
  23:      /// Number of seconds to take to revolve around the target object.
  24:      /// </summary>
  25:      public float duration = 5f;
  26:      
  27:      /// <summary>
  28:      /// Number of units away from object to place camera.
  29:      /// </summary>
  30:      public float distance = 10f;
  31:      
  32:      #endregion
  33:      
  34:      #region Events
  35:      
  36:      /// <summary>
  37:      /// We're assigning the singleton during awake, since its the 
  38:      /// earliest access we have to the MonoBehaviour object instance.  
  39:      /// Note that the .instance property is write-once, meaning that 
  40:      /// once it has an instance additional calls will not overwrite 
  41:      /// the original reference.
  42:      /// </summary>
  43:      void Awake()
  44:      {
  45:          Singleton<MyScript>.instance = this;
  46:      }
  47:      
       

The Awake() Unity event is chosen for assigning the singleton because this is the earliest point that we can guarantee the this pointer to be valid. As long as no access to this singleton is called before Start(), and this component is enabled, we can guarantee assignment of the singleton.

As mentioned in the code comments, the singleton instance is only written the first time, and all other redundant write calls are ignored. This means if for some reason multiple components with the same singleton exist in your Unity project (I would consider this a design mistake), the first component to call Awake() will be the instance used by the singleton.

       
  48:      /// <summary>
  49:      /// Use the active camera by default, and the game object attached to
  50:      /// this component to look at.
  51:      /// </summary>
  52:      void Start()
  53:      {
  54:          this.targetCamera = Camera.main;
  55:          this.target = this.transform;
  56:      }
  57:      
  58:      /// <summary>
  59:      /// Updates object position.  I'm using trig functions here, but 
  60:      /// you could use a quaternion to achieve the same effect.
  61:      /// </summary>
  62:      void Update()
  63:      {
  64:          this.theta += Time.deltaTime / this.duration;
  65:          
  66:          Vector3 offset = new Vector3();
  67:          offset.x = Mathf.Cos(this.theta) * this.distance;
  68:          offset.y = Mathf.Sin(this.theta) * this.distance;
  69:          offset.z = 0;
  70:          
  71:          Vector3 v = this.target.position + offset;
  72:          
  73:          this.targetCamera.transform.position = v;
  74:          this.targetCamera.transform.LookAt(this.target);        
  75:      }
  76:      
  77:      #endregion
  78:      
  79:      #region Private
  80:      
  81:      /// <summary>
  82:      /// Current angle around target.
  83:      /// </summary>
  84:      private float theta = 0f;
  85:      
  86:      #endregion
  87:  }
  88:   
  89:  /// <summary>
  90:  /// This class uses the singleton to access the distance property.
  91:  /// </summary>
  92:  public class MyOtherClass : MonoBehaviour
  93:  {
  94:      /// <summary>
  95:      /// Draws two buttons to move the camera closer and further away
  96:      /// from the target object.
  97:      /// </summary>
  98:      void OnGUI()
  99:      {
 100:          GUILayout.BeginVertical();
 101:          if (GUILayout.Button("Closer"))
 102:          {
 103:              Singleton<MyScript>.instance.distance--;
 104:          }
 105:          if (GUILayout.Button("Further"))
 106:          {
 107:              Singleton<MyScript>.instance.distance++;            
 108:          }
 109:          GUILayout.EndVertical();        
 110:      }
 111:  }
 112:      
       
Code Example 2b Unity example using GSD Software's generic Singleton class.

As mentioned before, this example uses a class from my company's internal library of Unity tools. The code listing for this class is displayed below, and is also available for download at the end of this page. Note that in order to test this code within Unity, you would need to split the above code into two files, since Unity requires any attached components to be contained in files with the same name as the component's class.

source code view: formated | plain text
       
   1:      
   2:  using UnityEngine;
   3:   
   4:  /// <summary>
   5:  /// Simple implementation of a singleton instance for any script.
   6:  /// </summary>
   7:  public class Singleton<T> where T : MonoBehaviour
   8:  {
   9:      /// <summary>
  10:      /// Write-once, read-many object reference.  Set this value at least 
  11:      /// once and all objects will have access to it as a singleton.
  12:      /// </summary>
  13:      public static T instance
  14:      {
  15:          get { return _instance; }
  16:          set { if (_instance == null) _instance = value; }
  17:      }
  18:   
  19:      /// <summary>
  20:      /// Returns true iff a singleton has been assigned for this class.
  21:      /// </summary>
  22:      public static bool assigned
  23:      {
  24:          get { return instance != null; }
  25:      }
  26:   
  27:      /// <summary>
  28:      /// current object reference
  29:      /// </summary>
  30:      private static T _instance = null;
  31:  }
  32:   
  33:  /// <summary>
  34:  /// Avoids unity compiler warning in Unity 2.5 and earlier.
  35:  /// </summary>
  36:  public class Singleton {}
       
Code Example 2c Singleton.cs from GSD Software's internal library.

You will notice that this singleton object is not a subclass of MonoBehaviour. Code developed within your Unity assets folder do not all have to be components, placing normal c# classes anywhere in your assets folder will allow the components from your project to access these classes as desired.

Assigning Objects Through the Inspector

Using the inspector to connect your objects together is probably the most obvious way to connect your objects together. Selecting the object with your script as a component lets you visually inspect the references to other objects, as well as clicking on the references for it to highlight the referenced object.

The biggest drawback to this method is that you have to always manually assign the object references each type you add another script the the scene. The actual reference itself is a standard c# reference, so there is no performance loss to this mechanism, just a potential maintenance nightmare. I have personally seen more than one person writeoff Unity in a message board as unmaintainable due to this issue.

source code view: formated | plain text
       
   1:   
   2:  using UnityEngine;
   3:   
   4:  /// <summary>
   5:  /// Example of accessing different objects through references setup
   6:  /// in the game object inspector.
   7:  /// </summary>
   8:  public class AssignExample : MonoBehaviour
   9:  {
  10:      #region Properties
  11:      
  12:      /// <summary>
  13:      /// Link to a camera setup in the inspector.
  14:      /// </summary>
  15:      public Camera targetCamera;
  16:      
  17:      /// <summary>
  18:      /// Link to a specific component type.
  19:      /// </summary>
  20:      public OtherComponent otherTarget;
  21:      
       

Notice how the reference for AssignExample.otherTarget uses the specific class "OtherComponent"? This makes it easier to setup the references for the component, as only game objects containing components of type AssignExample will be available from the drop down list in the inspector.

       
  22:      /// <summary>
  23:      /// How often to call other component.
  24:      /// </summary>
  25:      public float callFrequency = 1f;
  26:      
  27:      #endregion
  28:      
  29:      #region Events
  30:      
  31:      /// <summary>
  32:      /// Checks that all references have been assigned, outputting log
  33:      /// errors if any are missing and deactivates.
  34:      /// </summary>
  35:      void Start()
  36:      {
  37:          if (this.targetCamera == null)
  38:          {
  39:              Debug.LogError("targetCamera is not set");
  40:              this.enabled = false;
  41:          }
  42:          if (this.otherTarget == null)
  43:          {
  44:              Debug.LogError("otherTarget is not set");
  45:              this.enabled = false;
  46:          }
  47:      }
  48:      
  49:      /// <summary>
  50:      /// Points the camera at the other component and calls a method
  51:      /// on OtherComponent with the desired frequency.
  52:      /// </summary>
  53:      void Update()
  54:      {
  55:          this.targetCamera.transform.LookAt(this.otherTarget.transform);
  56:          if (Time.time - this.lastCall > this.callFrequency)
  57:          {
  58:              this.otherTarget.SetLastCalled(Time.time);
  59:              this.lastCall = Time.time;
  60:          }
  61:      }
  62:          
  63:      #endregion    
  64:      
  65:      #region Private
  66:      
  67:      /// <summary>
  68:      /// Last time OtherComponent was called.
  69:      /// </summary>
  70:      private float lastCall = 0f;
  71:      
  72:      #endregion
  73:  }
  74:   
       

Once the references are assigned, calls are made directly at full performance. This is definitely a high-performance option, but is difficult to manage for complicated projects.

       
  75:  /// <summary>
  76:  /// Class that AssignExample will talk to.  This code will need to be
  77:  /// places in a separate .cs file before testing in Unity. (Already
  78:  /// done for you in the downloads area.)
  79:  /// </summary>
  80:  public class OtherComponent : MonoBehaviour
  81:  {
  82:      #region Methods
  83:      
  84:      /// <summary>
  85:      /// Changes the value to display for this component.
  86:      /// </summary>
  87:      public void SetLastCalled(float f)
  88:      {
  89:          this.lastCalled = f;
  90:      }
  91:      
  92:      #endregion
  93:      
  94:      #region Events
  95:      
  96:      /// <summary>
  97:      /// Draw the last value passed in the upper-right corner.
  98:      /// </summary>
  99:      void OnGUI()
 100:      {
 101:          GUILayout.BeginVertical();
 102:              GUILayout.BeginHorizontal();
 103:                  GUILayout.FlexibleSpace();
 104:                  GUILayout.Label("Value=" + this.lastCalled);
 105:              GUILayout.EndHorizontal();
 106:              GUILayout.FlexibleSpace();
 107:          GUILayout.EndVertical();
 108:      }
 109:      
 110:      #endregion
 111:      
 112:      #region Private
 113:      
 114:      /// <summary>
 115:      /// Last time value passed from AssignExample. 
 116:      /// </summary>
 117:      private float lastCalled = 0f;
 118:      
 119:      #endregion
 120:  }
       
Code Example 3 References By Inspection

You could also point otherTarget to a base class when several kinds of objects are desired as candidates for the reference. This includes Behaviour and Transform classes, but this results in the drop down list being populated by all components in the current scene, which is usually an unmanageable situation.

Reference Loading with GameObject.Find()

Rather than connecting objects manually, you can use methods from the Unity API to connect up by name and/or type of script. This results in a performance hit when loading the references, but isn't usually a problem as long as the results are cached.

You would never want to lookup the reference to a component from any frequently call c#. Once the reference is loaded, method calls are performed directly, which is the fastest way possible.

source code view: formated | plain text
       
   1:      
   2:  using UnityEngine;
   3:   
   4:  /// <summary>
   5:  /// Example of interacting between objects using a name lookup, if
   6:  /// a reference hasn't already been assigned in the inspector.
   7:  /// </summary>
   8:  public class HybridLookupExample : MonoBehaviour
   9:  {
  10:      #region Properties
  11:      
  12:      /// <summary>
  13:      /// Object to point camera at.
  14:      /// </summary>
  15:      public Transform targetView;
  16:      
  17:      /// <summary>
  18:      /// Camera to point at object.
  19:      /// </summary>
  20:      public Camera targetCamera;w
  21:      
  22:      #endregion
  23:      
  24:      #region Events
  25:      
  26:      /// <summary>
  27:      /// Load references by name if not already assigned.
  28:      /// </summary>
  29:      void Start()
  30:      {
  31:          if (this.targetView == null)
  32:          {
  33:              GameObject g = GameObject.Find("target");
  34:              if (g != null)
  35:              {
  36:                  this.targetView = g.transform;
  37:              }
  38:          }
  39:          if (this.targetCamera == null)
  40:          {
  41:              GameObject g = GameObject.Find("camera");
  42:              if (g != null)
  43:              {
  44:                  this.targetCamera = g.camera;
  45:              }
  46:          }        
  47:      }
  48:      
  49:      /// <summary>
  50:      /// Keep camera pointed at object.
  51:      /// </summary>
  52:      void Update()
  53:      {
  54:          // early exit for missing references
  55:          if (this.targetView == null) return;
  56:          if (this.targetCamera == null) return;
  57:          
  58:          // point camera
  59:          this.targetCamera.transform.LookAt(this.targetView);
  60:      }
  61:      
  62:      #endregion    
  63:  }
       
Code Example 4 Looking up missing references by name.

Unity has the following methods to search for objects by different data types:

Using Static Properties and Methods

Calling class (i.e. "static") methods and properties in c# is the same speed as instance properties and methods, which are the fastest ways to communicate between objects. The biggest disadvantage of using static methods rather than a singleton is that the code will likely need to be rewritten if ever there is a need for a second instance of a particular object with different settings.

source code view: formated | plain text
       
   1:   
   2:  /// <summary>
   3:  /// Exposes static methods for other objects to communicate with.
   4:  /// </summary>
   5:  public class StaticLink : MonoBehaviour
   6:  {
   7:      /// <summary>
   8:      /// Draws the current update count.
   9:      /// </summary>
  10:      void OnGUI()
  11:      {
  12:          GUILayout.Label("UpdateCount=" + updateCount);
  13:      }    
  14:      
  15:      /// <summary>
  16:      /// Called by other objects to increate updateCount.
  17:      /// </summary>
  18:      public static void IncrementUpdateCount()
  19:      {
  20:          updateCount++;
  21:      }
  22:      
  23:      /// <summary>
  24:      /// Number of times IncrementUpdateCount() has been called.
  25:      /// </summary>
  26:      private static int updateCount = 0;
  27:  }
  28:   
  29:  /// <summary>
  30:  /// Communicates with StaticLink through its static methods.
  31:  /// </summary>
  32:  public class OtherClass : MonoBehaviour
  33:  {
  34:      void Update()
  35:      {
  36:          StaticLink.IncrementUpdateCount();
  37:      }
  38:  }
       
Code Example 5 Communicating with static methods

Using Unity Messaging

Unity has a built-in system for passing messages between components on the same game object without references or strong typing. By using Component.SendMessage(), Unity attempts to call the specified method on all components attached to the same game object the component calling SendMessage().

This mechanism allows for argument passing, and by default prints an error if no component receives the message. In conjunction with GetComponent() or FindComponent(), messages can be passed between any two objects in the scene, albeit less efficiently than through more direct mechanisms.

source code view: formated | plain text
       
   1:  /// <summary>
   2:  /// Same example as StaticLink, but using SendMessage() to 
   3:  /// communicate instead.  This requires both components to
   4:  /// be attached to the same object.
   5:  /// </summary>
   6:  public class MessageLink : MonoBehaviour
   7:  {
   8:      /// <summary>
   9:      /// Draws the current update count.
  10:      /// </summary>
  11:      void OnGUI()
  12:      {
  13:          GUILayout.Label("UpdateCount=" + updateCount);
  14:      }    
  15:   
  16:      /// <summary>
  17:      /// Called via SendMessage() to increate updateCount.
  18:      /// </summary>
  19:      public void IncrementUpdateCount()
  20:      {
  21:          updateCount++;
  22:      }
  23:   
  24:      /// <summary>
  25:      /// Number of times IncrementUpdateCount() has been called.
  26:      /// </summary>
  27:      private static int updateCount = 0;
  28:  }
  29:   
  30:  /// <summary>
  31:  /// Communicates with MessageLink through SendMessage()
  32:  /// </summary>
  33:  public class OtherClass : MonoBehaviour
  34:  {
  35:      void Update()
  36:      {
  37:          SendMessage("IncrementUpdateCount");
  38:      }
  39:  }
       
Code Example 6 Communicating with SendMessage()
Figure 2 Relative performance of different .Net call types

The first class from the code sample above will call the method on the second class, if both are attached to the same game object. Behind the scenes, [I haven't decompiled Unity to confirm this, but I suspect that] .net reflection will be used to issue the method call to the other object.

For relatively rare calls, SendMessage() is unlikely to be the biggest bottleneck in your application, since you are likely doing millions of polygon operations every second.

Rick Strahl, one of the most prolific .Net bloggers and Microsoft MVP, has an article about the relative performance of using reflection for method calls in this article on his blog.

The bottom line is it's about 3.5x to 4x slower than a direct method call. If you're spending 0.001% of your time doing this, that's not a problem. If you start spending over 1% of your time doing this, you might want to consider an alternative messaging mechanism.

Downloads for this Article

Drop the source code file anywhere in the Assets/ folder of your Unity project to enable this convenient functionality.
Note: The line breaks represent files that go together for each example.

Other Articles

Be sure to stay tuned to Unity Essentials Weekly. We'll be releasing weekly articles on how to be a better, cleaner, and more efficient c# Unity programmer. A simple camera manager will be one of many topics that will expand upon the lessons from this article.

The Unity Essentials Weekly blog is brought to you by GSD Software under our Unity Essentials brand name.

This product is presented to you free of charge, in the hope that spreading knowledge about Unity will lead to more development using Unity, which will lead to more projects for our company involving Unity, because we think it's the best thing since slice bread as far as 3D platforms go.

GSD Software is a Tempe, Arizona based company, specializing in web development, desktop software development, and virtual worlds.