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.
To implement delegates and events, follow these steps:
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); } } }
make things <color=green>GREEN</color>
, make things <color=blue>BLUE</color>
, and make things <color=red>RED</color>
.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; } }
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; } }
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.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: An easy to understand video about Unity delegates and events can be found at http://www.youtube.com/watch?v=N2zdwKIsXJs.
18.116.50.87