Very High Quality explanations, you deserve much more audience. I found it super super useful, thank you very much. I suggest you to make more video like this, in which you could give us some other advices about design pattern or good programming practices, because I think you have very good teaching qualities
Another problem that comes with using GameObject Singletons is "scene ownership". Your singleton lives in a particular scene, and every time you want to test another scene in isolation that has references to that singleton, it's a pain in the butt. Basically you need to add all the "Managers" and global instances in order to have a working scene. I found myself using less and less the singleton pattern for this exact reason. Lately I've been switching to ScriptableObjects to hold global state. The neat thing is you can make them as singletons too! if you want! But this time, if you put them in a Resources folder and load them from here, you can access them everywhere, every-time, without the need of a scene instance.
Very cool, I've been doing something similar. Though in my case I have a global manager which has a reference to a scriptable object. Is that what you mean by making the scriptableobject itself as Singleton? Otherwise I don't know if I'm too sure how to do that
@@matthewventures No, I meant a static reference to a class derived from ScriptableObject. I wrote an article about that subject, but it's in French. You could try to google translate it I guess, and code snippets are in English: www.samuelduval.fr/posts/scriptable-object-singletons/ It's based on a talk by Richard Fine if you're interested. It's called "Overthrowing the MonoBehaviour Tyranny in a Glorious ScriptableObject Revolution".
I know that this video is about a year old or more. But even at that time, Unity wasn't exclusively a single-threaded engine. As soon as you step into the realm of the Scriptable Render Pipeline, which the HDRP and URP are members of, You then start to see Unity relying more on the burst compiler and the creation of multithreaded jobs. Even though Unity does a good job at protecting the user, there are still conditions where these patterns could be an issue. Especially if you start using the job system yourself.
Thank you for the great explenation. Now at least I know the name of something I´ve been using the whole time and even funnier, I thought I invented this pattern XD Thank you sir for your time and efforts :)
You're welcome. You can checkout the "Programming Patterns " section of my book here: tinyurl.com/gamedevStudySheet It will cover these patterns and others that may be of interest to you.
I've changed this into a static class that has a Register method for my main systems (AudioSystem, SaveSystem etc) to register themselves and I get references on Start() at my custom scripts
@@matthewventures i have a TryGet method that returns bool and outs the MonoBehaviour, when not registered it just returns false. Main systems Register themselves at Awake (so they're available to access at Start) and Unregister at OnDestroy or OnDisable
I did this because I don't like having Manager singleton instances in my scene, they're annoying to manage across multiple scenes. It's also shorter to access instead of ServiceLocator.instance.GetService, I just type ServiceLocator.TryGet. Also more performant as there's no FindObjectOfType calls when reference not found
Really like how you've gone into the details on this. Never thought of SerializeField references being problematic when more than one person is working on a project. Service Locators are a new pattern for me too. Seems useful but is it actually different to just using FindObjectOfType, which is known to be super slow ? (apart from the caching you're doing) My main fear with it is that you're not forcing a single instance of these 'Services' which seems like a recipe for disaster (especially in the context of more than one programmer working on a project) You have a great knack of clearly explaining deep programming concepts, I'd love to see you do a video on Dependency Injection. Had a quick Google but it all seemed a bit over my head. :)
Hey, thanks for the insightful comment. Find object definitely gets a bad reputation for being slow, but it's honestly fine if you're only doing it once. When it becomes a problem is when you're doing it multiple times per frame. As I have written it, the service locator is as you said basically just a machine for doing find object in cashing, however I would suggest you check out other implementations of the pattern because there are more advanced add ons that you can add to a service locator in order for it to do more. One such example is the idea that you can return a null service. What that means is that, if maybe you cannot find a service you can still return one and this service instead of being truly know is instead a service that is sort of empty and does nothing. So for example this might be used when a game is muted, instead of returning a sound machine that is a null pointer, this kind of service locator would return a sound machine that does not play any sounds. Basically, the idea is that if we build the service locator who is in control of obtaining things then we have control over what we can provide and we can do some fancy tricks like that. Maybe I will make a DI video, that could be fun. You bring up a good point about how the current code does not assert that there is one service of each kind. This is actually very easy to do and I've done it in prior implementations. Basically just replace FindObject with FindObjects (note the plurality) and then assert if the array returned is not equal to length one. If it is length one, then grab the 0th index and continue on as normal. Thanks for watching and contributing these great points.
The issue is that Singletons are globally accessible. So it's the same ideology behind Globals are bad. If it's not well-structured, everything could be calling everything which leads to a relationship diagram which resembles spaghetti. It's hard to understand. Multi-threaded environment it can be hard to control
@@matthewventures Thanks for the answer, is it a performance issue or a project navigation and code readability issue? I've just never gotten to the bottom of this
Not a big fan of the explanation. Doesn't clarify why a bunch of Singletons is worse than the Service Locator pattern. Yes, Singletons are globally accessible which is bad but this is the case with both patterns so...
Hey thanks for the comment. Sorry my explanation fell short. Here's a link to a more detailed explanation gameprogrammingpatterns.com/service-locator.html if I said that Singletons are worse than service locators then that is false. These are just different approaches to solving the same problem and the best approach highly depends on your use case. The development environment is extremely relevant, because something like Unity where the c-sharp runs in a single threaded system has very different needs than a multi-threaded AAA engine. In my current Unity projects, I use a service locator which is a Singleton. So there are many hybrid approaches as well.
Very High Quality explanations, you deserve much more audience. I found it super super useful, thank you very much.
I suggest you to make more video like this, in which you could give us some other advices about design pattern or good programming practices, because I think you have very good teaching qualities
Another problem that comes with using GameObject Singletons is "scene ownership". Your singleton lives in a particular scene, and every time you want to test another scene in isolation that has references to that singleton, it's a pain in the butt. Basically you need to add all the "Managers" and global instances in order to have a working scene.
I found myself using less and less the singleton pattern for this exact reason. Lately I've been switching to ScriptableObjects to hold global state. The neat thing is you can make them as singletons too! if you want! But this time, if you put them in a Resources folder and load them from here, you can access them everywhere, every-time, without the need of a scene instance.
Very cool, I've been doing something similar. Though in my case I have a global manager which has a reference to a scriptable object. Is that what you mean by making the scriptableobject itself as Singleton? Otherwise I don't know if I'm too sure how to do that
@@matthewventures No, I meant a static reference to a class derived from ScriptableObject. I wrote an article about that subject, but it's in French. You could try to google translate it I guess, and code snippets are in English: www.samuelduval.fr/posts/scriptable-object-singletons/
It's based on a talk by Richard Fine if you're interested. It's called "Overthrowing the MonoBehaviour Tyranny in a Glorious ScriptableObject Revolution".
Very solid work! ServiceLocator is an underutilized technique (compared to pure Singletons).
I know that this video is about a year old or more. But even at that time, Unity wasn't exclusively a single-threaded engine. As soon as you step into the realm of the Scriptable Render Pipeline, which the HDRP and URP are members of, You then start to see Unity relying more on the burst compiler and the creation of multithreaded jobs. Even though Unity does a good job at protecting the user, there are still conditions where these patterns could be an issue. Especially if you start using the job system yourself.
Thank you for the great explenation. Now at least I know the name of something I´ve been using the whole time and even funnier, I thought I invented this pattern XD Thank you sir for your time and efforts :)
You're welcome. You can checkout the "Programming Patterns
" section of my book here: tinyurl.com/gamedevStudySheet
It will cover these patterns and others that may be of interest to you.
I've changed this into a static class that has a Register method for my main systems (AudioSystem, SaveSystem etc) to register themselves and I get references on Start() at my custom scripts
What do you do if a service is requested before it has registered itself?
@@matthewventures i have a TryGet method that returns bool and outs the MonoBehaviour, when not registered it just returns false. Main systems Register themselves at Awake (so they're available to access at Start) and Unregister at OnDestroy or OnDisable
I did this because I don't like having Manager singleton instances in my scene, they're annoying to manage across multiple scenes. It's also shorter to access instead of ServiceLocator.instance.GetService, I just type ServiceLocator.TryGet.
Also more performant as there's no FindObjectOfType calls when reference not found
Really like how you've gone into the details on this. Never thought of SerializeField references being problematic when more than one person is working on a project. Service Locators are a new pattern for me too. Seems useful but is it actually different to just using FindObjectOfType, which is known to be super slow ? (apart from the caching you're doing) My main fear with it is that you're not forcing a single instance of these 'Services' which seems like a recipe for disaster (especially in the context of more than one programmer working on a project)
You have a great knack of clearly explaining deep programming concepts, I'd love to see you do a video on Dependency Injection. Had a quick Google but it all seemed a bit over my head. :)
Hey, thanks for the insightful comment. Find object definitely gets a bad reputation for being slow, but it's honestly fine if you're only doing it once. When it becomes a problem is when you're doing it multiple times per frame. As I have written it, the service locator is as you said basically just a machine for doing find object in cashing, however I would suggest you check out other implementations of the pattern because there are more advanced add ons that you can add to a service locator in order for it to do more. One such example is the idea that you can return a null service. What that means is that, if maybe you cannot find a service you can still return one and this service instead of being truly know is instead a service that is sort of empty and does nothing. So for example this might be used when a game is muted, instead of returning a sound machine that is a null pointer, this kind of service locator would return a sound machine that does not play any sounds. Basically, the idea is that if we build the service locator who is in control of obtaining things then we have control over what we can provide and we can do some fancy tricks like that. Maybe I will make a DI video, that could be fun. You bring up a good point about how the current code does not assert that there is one service of each kind. This is actually very easy to do and I've done it in prior implementations. Basically just replace FindObject with FindObjects (note the plurality) and then assert if the array returned is not equal to length one. If it is length one, then grab the 0th index and continue on as normal. Thanks for watching and contributing these great points.
Here's a nice DI tutorial I found: czcams.com/video/JgBmAkkLFBI/video.html
Man what a good video, damn. Loved it!
What is the reason for the two Equals() in the null check in your singleton implementation? I'm pretty sure "_instance==null" would've sufficed. :)
Sorry can't recall
Very good video! Could using a service locator with asynchronous methods lead to race conditions?
Yes definitely. It's very dangerous in that respect.
I hear "Singleton bad" everywhere but I don't hear anyone giving a clear answer as to why
The issue is that Singletons are globally accessible. So it's the same ideology behind Globals are bad. If it's not well-structured, everything could be calling everything which leads to a relationship diagram which resembles spaghetti. It's hard to understand. Multi-threaded environment it can be hard to control
@@matthewventures Thanks for the answer, is it a performance issue or a project navigation and code readability issue? I've just never gotten to the bottom of this
Not a big fan of the explanation. Doesn't clarify why a bunch of Singletons is worse than the Service Locator pattern. Yes, Singletons are globally accessible which is bad but this is the case with both patterns so...
Hey thanks for the comment. Sorry my explanation fell short. Here's a link to a more detailed explanation gameprogrammingpatterns.com/service-locator.html if I said that Singletons are worse than service locators then that is false. These are just different approaches to solving the same problem and the best approach highly depends on your use case. The development environment is extremely relevant, because something like Unity where the c-sharp runs in a single threaded system has very different needs than a multi-threaded AAA engine. In my current Unity projects, I use a service locator which is a Singleton. So there are many hybrid approaches as well.