Improving efficiency with delegates and events and avoiding SendMessage!

Optimization principal 2: Minimize actions requiring Unity to perform "reflection" over objects and searching of all current scene objects.

When events can be based on visibility, distance, or collisions, we can use such events as OnTriggerExit and OnBecomeInvisible, as described in some of the previous recipes. When events can be based on time periods, we can use coroutines, as described in other recipes in this chapter. However, some events are unique to each game situation, and C# offers several methods of broadcasting user-defined event messages to scripted objects. One approach is the SendMessage(…) method, which, when sent to a GameObject, will check every Monobehaviour scripted component and execute the named method if its parameters match. However, this involves an inefficient technique known as reflection. C# offers another event message approach known as delegates and events, which we describe and implement in this recipe. Delegates and events work in a similar way to SendMessage(…), but are much more efficient since Unity maintains a defined list of which objects are listening to the broadcast events. SendMessage(…) should be avoided if performance is important, since it means that 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 design pattern (pubsub). This is also known as the observer 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 that will publish new events when UI buttons are clicked. We'll create some UI objects, some of which subscribe to the color change events, so that each time a color change event is published, subscribed UI objects receive the event message and change their color accordingly. C# publisher objects don't have to worry about how many objects subscribe to them at any point in time (it could be none or 1,000!); this is known as loose coupling, since it allows different code components to be written (and maintained) independently and is a desirable feature of object-oriented code.

How to do it...

To implement delegates and events, follow these steps:

  1. Create a new 2D project.
  2. Add the following C# script class ColorManager to the Main Camera:
    using UnityEngine;
    using System.Collections;
    
    public class ColorManager : MonoBehaviour {
      public void BUTTON_ACTION_make_green(){
        PublishColorEvent(Color.green);
      }
    
      public void BUTTON_ACTION_make_blue(){
        PublishColorEvent(Color.blue);
      }
    
      public void BUTTON_ACTION_make_red(){
        PublishColorEvent(Color.red);
      }
    
      public delegate void ColorChangeHandler(Color newColor);
      public static event ColorChangeHandler onChangeColor;
    
      private void PublishColorEvent(Color newColor){
        // if there is at least one listener to this delegate
        if(onChangeColor != null){
          // broadcast change color event
          onChangeColor(newColor);
        }
      }
    }
  3. Create two UI Image objects and two UI Text objects. Position one Image and Text object to the lower left of the screen and position the other to the lower right of the screen. Make the text on the lower left read Not listening, and make the text on the right of the screen read I am listening. For good measure, add a Slider UI object in the top right of the screen.
  4. Create three UI buttons in the top left of the screen, named Button-GREEN, Button-BLUE, and Button-RED, with corresponding text reading make things <color=green>GREEN</color>, make things <color=blue>BLUE</color>, and make things <color=red>RED</color>.
    How to do it...
  5. Attach the following C# script class ColorChangeListenerImage to both the lower-right Image and also the Slider:
    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    
    public class ColorChangeListenerImage : MonoBehaviour {
      void OnEnable() {
        ColorManager.onChangeColor += ChangeColorEvent;
      }
    
      private void OnDisable(){
        ColorManager.onChangeColor -= ChangeColorEvent;
      }
    
      void ChangeColorEvent(Color newColor){
        GetComponent<Image>().color = newColor;
      }
    }
  6. Attach the following C# script class ColorChangeListenerText to the I am listening Text UI object:
    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    
    public class ColorChangeListenerText : MonoBehaviour {
      void OnEnable() {
        ColorManager.onChangeColor += ChangeColorEvent;
      }
    
      private void OnDisable(){
        ColorManager.onChangeColor -= ChangeColorEvent;
      }
    
      void ChangeColorEvent(Color newColor){
        GetComponent<Text>().color = newColor;
      }
    }
  7. With button-GREEN selected in the Hierarchy, add a new On Click() event for this button, dragging the Main Camera as the target GameObject and selecting public function BUTTON_ACTION_make_green(). Do the same for the BLUE and RED buttons with functions BUTTON_ACTION_make_blue() and BUTTON_ACTION_make_red() respectively.
  8. Run the game. When you click a change color button, the three UI objects on the right of the screen show all changes to the corresponding color, while the two UI objects at the bottom left of the screen remain in the default White color.

How it works...

First, let's consider what we want to happen—we want the right-hand Image, Slider, and Text objects to change their color when they receive an event message OnChangeColor() with a new color argument.

This is achieved by each object having an instance of the appropriate ColorChangeListener class that subscribes their OnChangeColor() method to listen for color change events published from the ColorManager class. Since both the Image and Slider objects have an image component whose color will change, they have scripted components of our C# class ColorChangeListenerImage, while the Text object needs a different class since it is the color of the text component whose color is to be changed (so we add an instance of C# scripted component ColorChangeListenerText to the Text UI object). So, as we can see, different objects may respond to receiving the same event messages in ways appropriate to each different object.

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, likewise each time ColorChangeListenerImage/Text objects are disabled, those methods are removed (-=) from the list of event subscribers.

When a ColorChangeListenerImage/Text object receives a color change message, its subscribed OnChangeColor() method is executed and the color of the appropriate 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 ColorChangeListenerImage/Text objects register or deregister their methods.

The ColorManager class displays three buttons to the user to change all listening objects to a specific color: 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.

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 classes ColorChangeListenerImage/Text match this argument signature and so are permitted to subscribe to the changeColorEvent in the ColorManager class.

Note

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

See also

Refer to the Cache GameObject and component references to avoid expensive lookups recipe in this chapter for more information.

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

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