Monster shadow

VIDEO

Object References in Unity - How to Communicate Between Scripts

Description:
As a new dev, keeping references to game objects you need can be a little confusing. Learn how to store game objects for later use as well as allowing external scripts access to your goodies. Instead of trying to find an object, shift your thought pattern a little and ask "should I already know where this object is?". ❤️ Become a Tarobro on Patreon: https://www.patreon.com/tarodev ========= 🔔 SUBSCRIBE: https://bit.ly/3eqG1Z6 🗨️ DISCORD: https://discord.gg/GqeHHnhHpz ✅ MORE TUTORIALS: https://www.youtube.com/tarodev 0:50 - Find Methods 1:45 - GameObject from Instantiate 2:33 - Component from Instantiate 3:49 - Multiple Components 4:59 - Static Instance 5:42 - Public Access Modifier 8:08 - Constructors 9:20 - MonoBehaviour "Constructor" (Init) 11:00 - OnCollision 11:50 - TryGetComponent 12:41 - GetComponent 13:03 - SendMessage 13:35 - Closing
ADD A COMMENT

Robin
Man, the Instantiate() thing blew my mind. I've been instantiating things as GameObjects and then getting the component for a year now. Glad I clicked on this video and found out there is a shorter path!
rhyspuddephatt
UnityEvents are amazing for decoupling components. You can use them to trigger music, animation, partials, other scrips. It means the code you write about health doesn't need to know about who is should tell, it doesn't know about UI or the rest of the world. This works best in prefabs. Suddenly you can have flexible components that you only couple when you make a prefab
Antonio mairlon ponte rocha
ty for all the vids, PLX dont stop doing! i'm learning so much!
Klemen Šavs
Great video. At 0:43 at line: [SerializeField] private GameObject _unitPrefab; how _unitPrefab get link to the unit prefab gameobject? C# editor shows that unity icon and unit name after.
Tarodev
It's set in the editor 😊 Just drag and drop it in
Adam Olesiak
Imo some kind of dependency injection is worth mentioning. Way cleaner than the singleton example, can have different implementations based on build platform or scene that you’re in without polluting the unit manager code or the code of its use cases
Tarodev
You're right, I wish I added DI to this video
Kevin Howell
Thank you, new dev. Super informative. Subbed!
ReginTheSmith
I only came here to talk about the fact that he’s using clash of clans font in the thumbnail
Tarodev
Well, it's a commercial font which clash of clans is using.
Trang 500k vào (chonbantinh.xyz)
23:21 Tính cẩn thận là mẹ đẻ của sự an toàn nhưng lại là con đẻ của nỗi sợ hãi.
Eleocraft
When I'm using static instances I like to use properties with a setter that throws an error when the class is instantiated a second time. Just to prevent errors. Also I would name the static variable singleton because that's what it is really.
Aaron Carter
I work with Unity professionally and these videos are high quality, no nonsense. A design pattern I like a lot and don't even know the name of (don't think it has one) is to add a static List<T> property in a MonoBehaviour. If the MonoBehaviour is called Unit it would look like this: public static List<Unit> AllUnits { get; private set; } = new List<Unit>(); The key is to add new objects when they're created or enabled and remove them when they're destroyed or disable. So you'd be adding them in either Start, Awake or OnEnable with List.Add and then removing them in OnDestroy or OnDisable with list.Remove. Which one you choose depends on the behavior you want the list to have -- whether you want disabled things to stay in the list or to be removed from it. Of course there's a bit of extra code needed like making sure you don't add an object more than once and stuff, but that's the gist of it. The end result is that now you have a static list in your code you can access at any time and find all objects that have a Unit component! You can query this list for all sorts of things, like maybe you want to find all your units whose health is < 30 so a healer unit can go to them and heal them or maybe you want all units whose health is > 80 to do an attack. This makes it easy! Of course, I advise programmers NOT to use Linq queries again and again in a game loop and never use them every frame. They can be slow and expensive when it comes to garbage collection if abused. So only use those things at key moments like when the game is starting or loading or every now and again when special events take place. Otherwise, you should write out queries manually in a static method or something using a for loop. Oddly enough, there are some cases where a complex Linq query will actually be faster than an algorithmic query a person writes by hand because Microsoft has optimized the hell out of that type of query. So definitely research and test things you're unsure about -- the nuances can be a bit complicated! In any case, this design pattern I showed here makes it easy to find every object with a certain Component (such as Unit) at any time by simply accessing your Unit.AllUnits list! You can take this a step further by adding the Observer Pattern to things and firing events like an OnSpawn and OnDelete (name them what makes the most sense based on how you're using your objects). Then other classes can subscribe to that event in case they need to know when these things are happening! 🙂
JB the WAR Villain
1 year in, Jnr Game Dev working to Intermediate next 2 months, bit nervous as feels like I've barely scratched the surface. You have so much great insight, such a big help. I look forward to all your vids
Brandon Koh
Too high above my pay grade lol
Kenneth Bailey
Thanks
DynastySheep
Good tips, I've been using singletons myself but from what I've heard from my colleagues it's not the best approach in dealing with communication between other scripts because of how global the use is. It could get messy with many singleton references so I mainly limit myself to 1 or 2 singletons per project using it in managers of course.
DynastySheep
@Tarodev Yeah I like to use singletons because of it being straightforward and easy to reference but I've also been informed about Events. Would be nice if you could cover that in your future tutorials. I'm really enjoying your videos and the way you explain things, it's really out there amongst the top dev tutorials on yt if not the best.
Tarodev
I would suggest not limiting your use of them so you can experience why people say they're bad first hand. I tend to think people just regurgitate what they hear from others without really understanding why. A bunch of singletons in a small to medium project is absolutely fine and chances are you won't run in any problems. I generally go the easy and quick route every time because most of the time it'll be just fine.
Roger Busquets Duran
I strongly discourage the usage of Singletons here, it works for small examples like this, but it will raise issues as the project grows.
Arconath Resonath
Amazing! Besides being old or a new dev, this has to do with being self-thought and being exposed to "harmful" tutorials a lot. THIS IS GOLD AND I LOVE YOU AND YOUR BEARD!
Tarodev
Thanks for the beard compliment ❤️
Léo
I just came across an interesting use case that completely contradicted your advice at 3:42! Not saying this to antagonize, but rather to share a funny case of “write code for what you need” and not based on some random advice. I wrote a movement behaviour that expanded some navmesh functionality, and I was using a Waypoint : MonoBehaviour object as the cached reference type. Waypoint is kind of a n-Ary tree node kind of class. But turns out that the moment I wanted to allow for the bots to chase each other and the player, I realised the mistake I made - Had to refactor the entire code to use Transforms instead, otherwise requiring any and all game objects that needed to be chased to have the waypoint component - creating unnecessary memory overhead, and unnecessary complexity. Still needed to access the neighbour nodes in some instances, but a few get components here and there outside of the update loop can’t hurt that much? Oh well Fun times!
Salvador Novo Fillol
Is this dependency injection?
Tarodev
It's not, but I wish I included it in this video!
Mark
I look forward to the day I would be bored shitless by this kind of video. Until then, I'm just really happy to see someone that is reinforcing the 80% of what you said that I do know and giving a great intro to the 20% that I didn't know (constructors). This kind of thing should be Unity 101.
Turkus Gyrational
I love your videos. Would be really helpful to get one talking about different types of event systems. Delegates vs EventHandler vs Action (vs UnityEvents), that sort of thing.
SEE ALL COMMENTS


Transcript:

welcome to the video today i will be talking about referencing game objects
which is a area where a lot of new devs struggle uh
so hopefully i can remove some of the guesswork i see a lot of new devs doing
these like weird and wonderful tricks to reference their objects well not
wonderful they're disgusting so i'll show you what you should really
not be doing and then i'll show you what you should be doing and hopefully you'll
see the light so i've just got a unit manager here now i'm using a unit
manager this could be anything that spawns objects right it could be on the
ui you could be spawning buttons you could be uh like a weapon shelf whatever
okay i'm just using a unit manager all i've got here is a reference to a
game object my unit prefab so that we can start spawning objects so first
things first i'll just show you some bad ways to reference game objects so here
we've got game object find now if you're a new dev you're probably almost
certainly doing this this will search the entire hierarchy
for the object called character now computers are fast as hell okay so if
you've got like 10 or even 50 objects this is still going to be like blazingly
fast even though people will tell you it's slow
but really it's a bad way to do it it's it's it's messy okay and the other ways
that i'm about to show you are significantly faster same with find
object with tag uh it's just not a good way to do things this one here um
actually this is incorrect i copy and paste it from the web uh let's just say
unit right find objects with type this is one that
i do occasionally use all right so this one does have it's rare use case
but still you should avoid it if you can do so
so i'll get rid of these puppies
and let's start uh spawning objects so let's create a
spawned obj and this will just be instantiating our unit prefab so that
will obviously spawn the object and we'll see that right here called spawn
the object uh but now let's say we want to
do something with this spawned object here in the update loop
right so we need to store a reference to it so we can do that simply by doing
this okay so now we've created the private
reference of that and we can just come here and we can say uh spawned object
transform position equals random uh uh insider units fur so then
heading back to unity and pressing play chaos but uh it's doing what we need so
now we've got this uh we've spawned this object and now we've got a reference to
it and we can use it whenever the hell we want in this class
but what if it's not just a game object right like what if we've got this unit
here and we've got this unit script okay so
uh and let's say on the unit uh we've got a public int level uh variable or
something right and we want to actually grab and do
something with this level so what i see new devs doing all the
time is they store it as the game object and then later on when they need
something on the hero they'll do something like
spawned object dot get get component and they'll grab the unit and then like
they've got access to the level uh but this is a backwards as hell where to do
it as unit is a derived from monobehaviour you can simply store it
the prefab as a unit and your saved variable as a unit right so now this
spawn spawned object is is the unit you can just say spawned
object level all right
so a little tip i remember when i first began i used to store them as game
objects until i realized oh wait a second
i'm an idiot and then i started storing them as those but i feel like it's a
it's a mistake that every new dev crea makes
and just remember now that we're actually storing uh units here and not
game objects we'll have to redo the reference here so let's just pull that
in cool
uh so that's good for one unit what if you're wanting to store a bunch of units
right so let's make a private list unit and let's say units and we'll just
actually initialize this straight away just to save time
and down here we'll go for and we'll do 10 iterations and we'll go units dot add
instantiate unit prefab
cool so now we've got a list of all of our units
um which means we can actually do something
with them in update loop so let's do a 4-h loop here units unit
and what we'll do is we'll actually take that
so this is going to be true chaos i don't know what this is going to look
like just one one big shape
yeah so now we've got references to all of our
units there excellent but what if we want to access these units from an
external class let's say we've got a uh like a map manager here
but this could be anything you want right
and we want to grab our units and do something here let's say we want to
actually move our units in the map manager for whatever reason
so we have a few choices with the unit manager say a whole bunch of things are
wanting to access it if that's the case we could make it a static instance by
making it a public static unit manager instance
and then uh down here a bit in our awake we could set instance equals to this
and if you don't know what a static class is
basically it's just going to give us access across our entire code base to
this instance here so what's happening is unit manager is being created
normally right and then as soon as it starts it's setting itself to this
global static instance so now over in our map manager we can do
something like unit manager.instance and uh we're not actually exposing
anything yet as both of these are private okay so how do we do that
well we could make this list here public right we could make this public and
let's just rename that to better suit the public convention
so then here we can say units right uh unit no that's meant to be units
it's a list cool so now we've got access to our
units here and we could uh do this like that
and that would work fine but this is bad design because uh this class here is
what's managing our units right we don't want another class to be able to for
example come here and say units equals new list do we and like completely screw
up what the hell the unit manager was doing
so what we can do is instead of making this public let's actually return this
to private cool so let's leave that as is we can do
a few things here to expose it we could say public list unit and we could call
this units and it just returns units right so this is now a
providing a read-only list here so i wouldn't be able to do this now
equals new list okay because it's going to say
there's no setter available so an alternate way to do this and in my
opinion preferable is we can delete that let's make this public again and rename
this to units and now we can actually turn this into a
ghetto with a private setter so we can just go get
and we'll have a private set um and this will be exactly the same as
those two lines effectively so here uh it'll still work the same we can't set
to it but we can in fact read it uh the the units from the from the list
and by the way this here is the same as like just defaulting it i like to
actually explicitly have it there so just to
demonstrate um no we don't have a map manager so map
manager and put the map manager on now you'll
see awake will be called the instance will be set map manager will now be able
to access all of those units so yeah there you go okay so this is
good in the scenario where a whole bunch of different classes are going to be
accessing it but what if that's not the case what if you want to keep it a
little bit more locked down right well we can use something like a constructor
for that and actually send in this reference so if you've only been
programming inside unity you probably haven't seen or used constructors all
that much so let's just say we've got a public class we'll call this r class
in here let's have a property this will be a string of name and then we'll also
make a constructor uh which will take in a string name
and then in here we'll set name equals name and this is just an example of a
constructor if you didn't actually know so we would say r class equals new
class and then in here we could say like tarot dev and that would now feed in
this into our constructor and it's basically just setting up a class so
constructors are just a way to initialize something with a default
value uh a default set of values and uh just set up the class in general right
in unity with game objects we don't actually have access to a constructor
because we're not constructing it ourself we're actually calling
instantiate and uh unity does a whole bunch of stuff on the c plus plus side
actually spawns the object um and they they would call their own constructors
and stuff right so yeah we don't get the actual constructor but what we can do is
create our own little constructors pseudo constructors
so in here we would have we can't use the actual real constructor as unity
will throw a fit but what we can do is we can say public void and i like to
call mine init and now here
so instead of creating a static instance we could
actually give this unit direct access to our unit manager by doing something like
this unit manager manager and then manager equals manager right so
we're saving we're saving this instance on our class
and now here in our unit manager when we're actually spawning our object so
let's just grab that and go var unit equals this
and then unit.net right and we're sending in our unit manager so we're
actually passing the reference into them and then we'll actually add it to the
list so now uh our unit has access to this
manager but let's just assume this is in here it's still locked down right we're
still locking down the unit manager only the things that really need it have
access to it so then for example later on here um we could report to the unit
manager that we're moving to another waypoint or
if we just died we can actually report to the unit manager hey we died uh
cleaners up remove us from the list something like that so that's two good
ways to reference objects between scripts uh one way is if it's massively
asset accessed by a whole bunch of different classes and another way is to
keep it nice and um nice and kind of like locked away just passing the the
reference to the exact things that need it i do want to show you one more thing
on the unit let's say we're doing some collision detection so uh on collision
enter right and let's say we're looking for other
units so if we're running into another unit we know that it's a unit right
because let's say we're doing some uh we're doing some checks here we're
saying if collision compare tag oh game object compare tag is on the unit tag or
whatever so we're doing these checks um but we don't actually know what scripts
are going to be on this unit so let's just assume we're using composition so
some units will have a script which will be like um explode
on touch another one might have heal on touch or whatever so let's actually do
that let's create a script here called uh heal on touch
so then in here say we only want to run specific logic if the other unit has
heal on touch so what we could do is we could say if
uh collision
game objects try get component and then in here we
can use the out keyword and it will be heal on touch
heal on hot
cool so now what this function is going to do is it's going to return true or
false if it actually if the other script was actually found on this unit okay and
it's going to give it to us as an argument using the out keyword so
we're checking to see if it's got it if it does then this will run and then like
you know heal and touch may have some function called uh
public void heal unit something something something so then we
can uh go hot dot heal unit
or something you know so that's a really nice way to do it um
you've probably done it something like this before uh
heal on touch and then you'll do something like collision transform get
component heal on touch and then you do something
like if h does not equal null then let's do this uh but yeah this is just a much
nicer way to do it um also uh the other day i completely forgot
that this even existed but i accidentally tabbed it out uh what was
it uh gameobject.sendmessage right i forgot this existed uh and
probably because it's so horrible if you're using send message stop
immediately it's not slow or anything i actually benchmarked it it's actually
insanely fast but yeah just don't use it this is faster and it's also cleaner and
it gives you uh you know type safety and stuff instead
of you having to do something like this uh yeah just stop using send message
there's literally never a reason for you to use send message but yeah that's it
so uh if you're a new dev hopefully this cut some corners for you and filled in
some gaps if you're an old dad this probably
brought the absolute out of you but yeah like the video subscribe if
you're feeling generous and want to support me go over and become a tarot
bro on my patreon i'd really appreciate it uh and until then i'll see you next
time bye you