The Most Fundamental Concept in Unity

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: Archions
Views: 29,936
Rating: undefined out of 5
Keywords: unity, unity3d, unity engine, unity tutorial, tutorial, c#, scripting, gamedev, indie dev, games, video games
Id: kETdftnPcW4
Channel Id: undefined
Length: 9min 50sec (590 seconds)
Published: Sat Feb 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.