Connecting scripts on the same object in Unity

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
when you're making your first game one of the challenges you're likely to face is how to connect scripts and objects together in a way that makes sense which is a pretty common problem and typically leads to using some kind of design pattern solution such as the Singleton events or Global variables but while working out how to pass data around your scene can be a challenge sometimes it can actually be more difficult to connect scripts that are on the same object building a single object with multiple interconnected systems such as a player object for example can be surprisingly difficult in this video I'm going to show you a simple method for connecting different systems together on the same object without making a mess of your project so what exactly is the point of all this what's the problem I'm trying to solve when you start making a game unfortunately there's a good chance that you might not finish it this can easily happen if the project becomes too difficult to work with which is why keeping your project as simple as possible can give you the best chance of actually seeing it through to release but what makes an easy project while there's no one answer a general rule of thumb is that a project that's easy to change is easy to work with so what does easy to change look like consider a player object when you're building it you might choose to add functionality by adding a number of systems where each system a script typically only does one thing this can be a good approach as it keeps each system self-contained and easy to work with however chances are the events of one system will have it effects on others for example an input system might trigger a movement system which might trigger a fuel system which might disable the movement system which at first glance might seem straightforward enough after all some systems only have a single purpose to work with another system or component so it makes sense to connect them directly but as you start adding other features to your player some scripts may need to respond to systems in your game While others might influence what they do which if you keep adding more and more direct connections to make that happen can make your script more dependent on each other meaning that when you do change something it's more likely to break so what's the alternative what if you could add or remove a component from your player object and it just works how you'd expect add an input script and the player can move turn off weapons and they can't fire delete a feature because you don't like it anymore nothing breaks building each system in a way that reduces its dependence on other systems but still allows them to communicate can make it much easier to change your projects which makes it much easier to work on and easier to finish so how can you do it in this example I'm going to show you how to connect the different scripts that make this player object work and I'm going to do it in a way that makes changing any of those scripts easier to do however keep in mind that this method just like any tutorial may not be right for everyone and it may not be right for you which is why I'm also going to explain the point of each step and different ways you could do the same thing so that you can decide for yourself if it's going to help you or if a similar method would work better for your game start with a single object and add a player script to it this script is very simple all it does is identify this particular object as a player in the game it's a modern behavior meaning that it can exist in the scene as a component attached to an object and represents the instance of the player in the world but it doesn't store any data about the player instead it holds a reference to another class the player ID the player ID is a scriptable object which means that it is technically an asset what that means is that it exists in the project not in the scene as a result any object in the game can access it in the same way as any other asset without having to search the scene for it or use a static reference to make one create a new script that inherits from scriptable object and include the create asset menu attributes above the class definition this will allow you to create an instance of the scriptable object type in your project folder in this case for player 1. the player one asset can then be used to store data which means that passing it to other systems in the game such as to the UI allows it to display information about that player specifically this works because the scriptable object asset exists before the player object does and will continue to exist after it's destroyed meaning that important information such as the number of lives the player has left can be kept track of even after the player dies while references to the player's information can be set up before it's even instantiated in the scene this kind of works like a local Singleton but without some of the drawbacks the general idea of a Singleton is that everything can easily access it usually by using a static reference to a single instance meaning that you won't need to search for the object in the scene the drawback is however that they usually can't be more than one of whatever it is you're using a Singleton for which is why player Singletons can sometimes cause problems if you later decide to add multiplayer instead using this kind of local Singleton means that there can be more than one player just duplicate the player object and create a second player ID for player 2 player 3 and so on this will create another instance of the player ID asset type meaning that even though the data is different it can still be passed around the scene interchangeably but so far in this example there isn't any data in the player ID asset and even if there was there's no way for the Player's systems to get a reference to it it's easy enough to get a reference to another script you can create a public or serialized variable and then drag the component that you want to it or when using scriptable object data just select the asset that you want to use however the point of this exercise is to make things easy and not having to set up every system with a reference to the player it's attached to is part of that instead the reference can be set up automatically in awake using get component and because you know where the player script is you only really need to search in one place the object's root which you can access directly using transform root however you probably don't want to have to write this out every time you make a new player system generally when different scripts need to do the same thing because they are the same type of component it's a good use case for inheritance this works by creating an abstract class called player system which because it's abstract can't be added to an object as it is instead another class will have to inherit from it in order to use it then create a protected virtual version of the awake function and place your get component call in there protected means that inheriting scripts will have access to the function while virtual means that it can be overridden however if you do override the awake function usually because the inheriting script also wants to do something in awake you'll need to call Base dot awake to make sure that the player reference is still set then simply replace mono Behavior with player system in any of the players child objects and the script will automatically set up a reference to the player class for you you'll then be able to access the player variable and through it the player ID asset from any player system every player system on the object such as the fuel system movement system or health system now has its own automatic reference to the player script meaning that it can easily access data about the player whenever it needs to but at the moment there is no data to access so where will that data come from and how can this method make communicating between scripts easier to do the problem with directly connecting scripts is that they become dependent on each other and while this is fine for scripts that only work with another component such as fading a Sprite or playing an audio Source trying to connect many different systems together manually can cause problems for example when the ship moves it might interact with the fuel system to reduce the fuel level however for this to work the movement system needs to hold a reference to the fuel system which may work well until the time comes to add a sound effect or a visual display which if you set them up in the same way means that the movement system now holds multiple direct connections to other scripts the problem with that is that the movement system is now very specific instead of just dealing with its own task movement it now also deals with several other systems as well taking responsibility for tasks that are rightfully none of its business which makes it much more complicated harder to work with and very difficult to change so what else can you do one option is to use a Unity event to connect with other scripts this can be useful for creating modular connections between local scripts in the inspector as it means that the movement script is now only responsible for its own task movement and for triggering an on thrust Unity event when it does that the unity event works as a single Anonymous output from the movement script and makes it much easier to change what else happens when this script runs without having to change its code the drawback is that you'll have to set up each connection yourself manually what's more Unity events are managed at the source not the receiving script meaning that if you want to prevent a function from being triggered by turning a script off for example you can't instead you need to disable it at its source or block functions on the receiving end with conditional checks so what else event delegates can be extremely useful for managing responses to other actions as they allow you to trigger a function when something else happens this works by having the movement script raise an event in this case an action which is a kind of ready-made delegate while the fuel system subscribes one of its functions to it then when the delegate is called any functions that are subscribed to it are also called in response subscribing scripts manage their own connections meaning that if they're turned off they stop responding to the event just as you'd expect them to which works fine but the fuel system still requires a connection to the movement system specifically in order to work meaning that if you were to replace the movement system with something else you'd need to change every script that accesses it too so what's the solution instead it's possible to use the player ID asset as a layer of abstraction between the two components meaning that the systems don't communicate directly this method still uses actions but instead of placing it in the movement script it's added to the player ID asset instead there are two ways you could do this either by adding it directly into the player ID or to keep things a bit more organized what I like to do is put it in a dedicated events struct if you're familiar with events and delegates you'll notice that this is not an event action meaning that it can be raised by a different class than the one it's declared in what this means is that the movement class will be able to invoke the action even though it's on a different script checking to see if it's null first whenever the movement system is used while a fuel class can subscribe one of its own functions to that action when it's enabled being careful to unsubscribe the function from the action if the script is disabled and so long as the function matches the return type and the parameter signature of the action it can be added to the action delegate in this case the action is void with no parameters as is the reduce fuel function however it's also possible to pass data through an action by specifying a data type when declaring it then by passing in a value of that type when invoking the action it's possible to pass information to receiving functions that have the same argument pattern such as to pass input data for example which can then be picked up by movement script to move the object the idea is that any system on your player object can report when it does something by invoking an action and any other system can listen out for it by subscribing the benefit of this method is that no two player systems are directly connected with each other one script does not know or care that another one exists you could remove them add them back in again or turn them off and nothing will break because they're not dependent on each other they simply listen for one or more of the player's actions without needing to get a reference to the system that raised it as a result it's easy to visualize and can be extremely useful for keeping things simple even with very complex systems for example the player input script might raise input events that the movement system listens for in order to move the ship around using this method it's possible to build an AI movement system that raises the same input events and because all of the information that the AI needs to make decisions can be read from other publicly available actions such as if the ship is hit if fuel or health is low or if they've got a more powerful weapon it's possible to Simply replace the player input script with the AI controller without making any new connections even while the game is running and it will work but there are drawbacks to this method for example typically you might not use actions in this way since the Observer pattern on which this method is based generally involves differentiating between the subject which is the script that raises the event from The Observer the script that listens for it however when using this method any script can either be the subject or the Observer so you'll need to be careful about which scripts are playing which role also placing system data outside of the system it relates to such as in the player ID asset can encourage building one script in two places making it harder to work with and increasing the risk that a different script will interfere with its data a general approach is to try to keep data local wherever possible using actions to share information with other scripts ideally the only data you should store on the scriptable object is persistent data such as information that's used to set up the player such as a name or color or data that will need to survive the object being destroyed such as the number of lives remaining while the player script the monobehavior that identifies the player object in the scene can be used to refer to a specific instance of the player while it's not a perfect system this method of managing player systems are an object can make your game a little easier to build and importantly easy to change as it allows you to add or remove systems from your object without worrying about what they're connected to and it makes extending your game by adding new functionality or adding extra players much more straightforward which if it works for you could just be the difference between finishing your game and not now I want to hear from you how do you keep your project easy to work with would this method help you or make things more complicated and what's your best tip for making Unity projects easier to put together whatever it is let me know by leaving a comment like this video if you found it useful and get subscribed for more videos from me I'll see you next time [Music]
Info
Channel: Game Dev Beginner
Views: 3,744
Rating: undefined out of 5
Keywords: unity, gamedev
Id: JAoZL1G_f2Y
Channel Id: undefined
Length: 15min 36sec (936 seconds)
Published: Wed Apr 26 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.