Improving efficiency with delegates and events (and avoiding SendMessage!)

When events can be based on distance or collisions, we can use OnTrigger methods as described in the preceding recipe. When events are based on time periods, we can use coroutines, as described in a later recipe in this chapter. When there are other kinds of events, we can use C# delegates and events, as described in this recipe. These work in a similar way to SendMessage(), but are much more efficient, since Unity has a defined list of the objects "listening" to the broadcast events. SendMessage() should be avoided, since it means Unity has to analyze each scripted object ("reflect over" the object) to see whether there is a public method corresponding to the message that has been sent—this is much slower than using delegates and events.

Delegates and events implement the publish-subscribe (pubsub) design pattern. Objects can subscribe one of their methods to receive a particular type of event message from a particular publisher. In this recipe, we'll have a manager class displaying some color change buttons and publishing a new event for each button clicked; we'll also have a cube and a sphere that subscribe to the color change events, so each time a color change event is published, both the cube and the sphere should receive the event message and change their color accordingly. Publishers don't have to worry about how many objects subscribe to them at any point in time (it could be none, or 1000!), this is known as "loose coupling", since it allows different code components to be written (and maintained) independently, and this is a desirable feature of object-oriented code.

How to do it...

To implement delegates and events, perform the following steps:

  1. Add the following C# script class ColorManager to the Main Camera:
    // file: ColorManager.cs
    using UnityEngine;
    using System.Collections;
    
    public class ColorManager : MonoBehaviour {
      public delegate void ColorChangeHandler(Color newColor);
      public static event ColorChangeHandler changeColorEvent;
    
      void OnGUI(){
        bool makeGreenButtonClicked = GUILayout.Button("make things GREEN");
        bool makeBlueButtonClicked = GUILayout.Button("make things BLUE");
        bool makeRedButtonClicked = GUILayout.Button("make things RED");
    
        if(makeGreenButtonClicked)
          PublishColorChangeEvent( Color.green );
    
        if(makeBlueButtonClicked)
          PublishColorChangeEvent( Color.blue );
    
        if(makeRedButtonClicked)
          PublishColorChangeEvent( Color.red );
      }
    
      private void PublishColorChangeEvent(Color newColor){
        // if there is at least one listener to this event
        if(changeColorEvent != null){
          // broadcast change color event 
          changeColorEvent( newColor );
        }
      }
    }
  2. Create a cube at (0, 0, 0), and attach the following C# script class ColorChangeListener to it:
    // file: ColorChangeListener.cs
    using UnityEngine;
    using System.Collections;
    
    public class ColorChangeListener : MonoBehaviour {
      void OnEnable() {
        ColorManager.changeColorEvent += OnChangeColor;
      }
    
      private void OnDisable(){
        ColorManager.changeColorEvent -= OnChangeColor;
      }
    
      void OnChangeColor(Color newColor){
        renderer.sharedMaterial.color = newColor;
      }
    }
  3. Create a new material named m_cube and add this to your cube.
  4. Create a sphere at (-5, 0, 0), and attach a ColorChangeListener component to it.
  5. Create a new material named m_sphere and add this to your sphere.
  6. Create a directional light, so we can see the cube and sphere colors easily.

How it works...

First let's consider what we want to happen—we want the cube and sphere to both change their color when they receive an event message OnChangeColor() with a new color argument.

This is achieved by each object instance of the ColorChangeListener class (there is one present as a component of the cube and one of the sphere), subscribing their OnChangeColor() methods to listen for color change events published from the ColorManager class. Since our scripted objects may be disabled and enabled at different times, each time a scripted ColorChangeListener object is enabled (such as when its GameObject parent is instantiated), its OnChangeColor() method is added (+=) to the list of those subscribed to listen for color change events. Then each time ColorChangeListener objects are disabled, those methods are removed (-=) from the list of event subscribers.

When a ColorChangeListener object receives a color change message, its subscribed OnChangeColor() method is executed, and the color of the shared materials of the renderer component is changed to the received Color value (green/red/blue).

The ColorManager class has a public class (static) variable changeColorEvent, which defines an event to which Unity maintains a dynamic list of all the subscribed object methods. It is to this event that ColorChangeListener objects register or deregister their methods.

The ColorManager class displays three buttons to the user to make things green, red, and blue. When a button is clicked, the changeColorEvent is told to publish a new event, passing a corresponding Color argument to all subscribed object methods (that is, the cube and sphere).

The ColorManager class declares a delegate named ColorChangeHandler. Delegates define the return type (in this case void) and argument "signature" of methods that can be delegated (subscribed) to an event. In this case, methods must have the argument signature of a single parameter of type Color. Our OnChangeColor() method in the ColorChangeListener class matches this argument signature, and so it is permitted to subscribe to the changeColorEvent in class ColorManager.

Note

An easy-to-understand video about Unity delegates and events can be found at http://www.youtube.com/watch?v=N2zdwKIsXJs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.72.176