When I first started making games in
Unity, and programming in C sharp, the biggest problem I had was how
to communicate between objects. I struggled to wrap my head around
how to pass information from one object to another. And when I say object
I mean from one script to another script To me, passing information around is the
most fundamental part of object oriented programming and when you truly understand the
concept of passing data or information between objects then you can essentially do anything
you want with programming and within Unity. Now usually, to pass information, or just
communicate with other objects you need a reference to the object you’re trying to speak to. As a side note, in Unity, we tend to have one
class per script and so I’m going to use the words class and script somewhat interchangeably, for
this video just think of them as the same thing. So operating within a class you pretty much
have access to everything else in that class either through direct referencing or passing
the information as arguments in a function. But once you start trying
to access and communicate with things outside your class it gets tricker. After all you can reference
the class itself as a type, but this doesn’t actually point to anything
specific, it’s just pointing to a blueprint. Think of it like this. If we have a bunch of balls
in a ball pit. Referencing the class is like referencing the
concept of a ball in our ballpit. Sure you have the concept of a ball
in your script now, but that’s just the concept. It’s not actually pointing
to any of the specific balls in the pit. So if we want to interact
with the balls in our pit, what we need is a way to point to the specific
instances, the specific balls, from our script. One way to do this is by assigning
references within the editor. I’ve got a scene in Unity with a Ball Manager
class and 4 balls each with a ball script on it. If our BallManager script wants to speak to our
balls, we could define a field for each ball, serialize it so that the
field shows in the editor, and then go and assign each ball to each field. If my script has pre-assigned references
to all the objects it needs to speak to then we can start to communicate between classes. And in many situations this works fine. But
this approach does limit us in a number of ways. First, if you have a lot of game objects,
then assigning all of these in the editor can be extremely tedious and also
gets pretty messy in the script. But the biggest problem with assigning objects in the editor is that this has to
be done before the game runs. If at runtime, which just means while the game is
running, we create a new ball, then our manager won’t have a reference to this new object, and
so it won’t be able to communicate with it. In these scenarios we have
to go with another approach. Suppose we’re in the opposite situation.
Instead of our manager talking to each ball, we want each ball to be able to
communicate with our manager. To do this we could use static classes. A static class utilises the static keyword
and makes the class globally accessible. This means that any script
anywhere within our game can access and communicate with the static class. If I want the balls to be able
to communicate with the manager, then I could make my ball manager a static
class, or create a static instance of it. These are two different things, but
in Unity it is generally more common to see static instances because this
allows us to instantiate the class and extend from MonoBehaviour which gives
us access to all of the Unity APIs. To make a static instance we define a
static variable of type Ball Manager and set it equal to this instance of our class If you’re not familiar with the static
keyword I would do some further research about it because it takes some time to
wrap your head around how exactly it works. Anyway, now that we have a static
instance of this class, all our balls can access it, using the static variable.
So now in our ball script we type the class, ball manager, then reference the static variable
we made, and boom, we now have a reference to the ball manager and we can call any public functions
or access any public variables that class defines. Which is all fine and dandy, but we still
haven’t solved the earlier problem. If we create a new ball at runtime, how can
we access it from our ball manager? The new ball will be able to speak to our
manager because our manager is a static instance and so it is globally accessible
from any class within our namespace, but our ball manager won’t have any
idea about this newly created ball. So why don’t we make our ball class static
so it’s globally accessible as well? Doing the same thing we define
a static variable of type Ball. As an aside the name of this variable doesn’t have
to be ‘instance’, it can be whatever you want, as long as it’s static and is the same type as
the class you want to make globally accessible. Now in our manager, we can reference the static
instance of the ball, and do things to it. For example, let's disable the ball. Doing this we might expect
all our balls to disappear. But this doesn’t happen, only one gets disabled. And this is because if you remember we need
to assign an instance to our static variable. And if every ball assigns itself to this
same variable they will overwrite each other. Because we can only ever have one
static instance for any given class. Which is a pretty big limitation and
puts us back to square 1 about how we can communicate with the
balls from our game manager. Using this static approach only works
on objects that have a single instance, so for our many balls, using
a static instance is not an option to enable communication
between our manager and balls. But all hope is not lost. For this, is where events can come in handy. In this case we can use events to
communicate between game objects without having direct references to each other. An event is exactly what it sounds like. We define a certain event and when that
event happens it gets fired, so to speak. Then, anyone that cares about that
event will be notified that the event just occurred and can do something accordingly. In this way we can indirectly communicate
between our ball manager and balls. So in our manager class we can
define a static event of type Action. In this example the event doesn’t actually
have to be static because we’ve already made our BallManager class static, so
it can be referenced via our static instance of this BallManager class
but I’m making it static regardless. Anyway, don’t worry too much about
that, instead let’s focus on the event, There are a few different types of events in C# but the most common one I use
in Unity is the Action type. I would encourage you to look at some of the
many videos explaining events in more detail, but for most use cases Action
type events will be sufficient. Then we will name the event. General convention is to begin
the name of an event with the word ‘On’ followed by a description of the event. So in the case of wanting to disable all our
balls, we’ll call the event ‘OnDisableBalls’. Now that we have our event we can go into our
ball script and ‘subscribe’ to that event. Any one that is subscribed to an event
will be notified when that event occurs. Subscribing to the event doesn’t trigger anything, all it does is make sure that this class is
listening for when the event does get triggered. The subscription of an event is done
when the object is first initialised so we’ll put it in the Awake function that is
called right when this object is instantiated. This is the syntax used to
subscribe to an event in C#. Now when subscribing to an event we
need to specify what function will be called when the event is actually triggered. So we’ll make a function in our class called
‘DisableMe’, and use this when subscribing. You should also always unsubscribe to the event if and when the object is destroyed to
avoid problems that I won’t go into now. So in the OnDestroy function, which funnily
enough is called when the object is destroyed, we unsubscribe to the event using this syntax. Ok, now we’re pretty much done. We have the event defined in our manager class,
and we’re subscribing to it in our ball class, ready to run the ‘DisableMe’
function when the event is called. The only thing left to do is actually trigger the
event. We can do this from wherever we want since the event is static but for this example it makes
sense to call the event from the manager script. So we’ll create a function called
‘DisableBalls’ that invokes the event. We use the null-conditional operator
given by the question mark and period, to avoid null reference errors if
no one is subscribed to this event. Then we simply call the invoke
function to trigger the event. And that’s the event done. If we make this function public we can hook up the ‘DisableBalls’ function to a button so we
can easily trigger the event when we want. And now we are communicating successfully
between our manager script and our ball script. While this might seem like a lot to set
up, the reason events are so powerful is because once you’ve created an event, you
can subscribe to it from anywhere you want. So if I wanted another part of my game to also be
aware that we’ve just disabled the balls we can simply subscribe to the DisableBalls event and
do something completely different from there. For example this game UI. I’ve subscribed to the disable balls event in it and so now when
the button is clicked the UI will change. The final reason events are so powerful is because we can use them to pass other information
to subscribers in the form of parameters. In our DisableBalls event, we
can modify the event signature like so to enable us to pass in an
integer when the event is triggered. Let’s define an integer field and serialize
it so we can modify it in the editor. And now when we invoke the event
we will pass in this number. Then in the ball script we can
define a ball number and modify our ‘DisableMe’ function that is run on the
DisableBalls event to take in an integer. Then we check if the integer received
is equal to our ball number and if it is we disable ourselves, if not we just ignore it. Now when we trigger the event only the
ball number we specified is disabled. Again we can modify our UI to utilise this
information and display text accordingly. That is how we use events in Unity to
communicate between objects and pass information enabling us to do,
essentially, anything we want.