Another approach is a static class to hold actions that can then be invoked anywhere. That likewise doesn't have to sit on a game object of worry about being destroyed. But decoupled code can make it difficult to track event chains, since a publisher has no record of its subscribers. You have to use your search parameters in your code IDE to tracks event chains. Spaghetti is horrible, but so is a partially obscured event system.
Thank you, this did so much more for me than the Unite Austin talk. I could not ever wrap my mind around what he was saying, but this is just a really well-done demonstration.
singletons are actually used in games more than most other programming fields because the usual downsides of singleton (global state) are not as bad in video games since you know your code will be run in a single contained application. singletons cause issues in projects like web servers which are doing loads of things simultaneously and having a single shared resource and single shared piece of state can cause loads of problems, in a game its much easier to manage global state and singletons are a good way to keep global state from getting messy. I'd also like to point out that your SOs are basically singletons implemented in a different way and have all the same issues with global state, plus some idiosyncracies with how SOs are implemented. The main advantage of this solution is you have avoided dependency chains, which is a problem that's already solved (most games will instantiate all their singletons at game boot time and set up dependency chains then) Event based programming also has it's own set of problems and downsides. However this is a useful strategy I hadn't seen before so thank you for the informative video.
There is a clear difference between non-Singleton SOs and Singletons [in the case of Unity, though I'm not sure what other game Engine also has SOs-- ] Domain Reload. Singletons require static referencing, so you must keep Domain Reload on to maintain a fresh state between Play Sessions in the Editor. SO have no such requirement since they are always considered loaded within the Editor, and when they are referenced [by a MonoBehaviour] in a Build. This difference means it is orders faster to test and iterate with non-Singleton SOs versus Singletons, especially as your project and assemblies grow larger. Edit: I'm being explicit about non-Singleton SOs, because you can implement SO's as Singletons. They're not mutually exclusive.
I like to make a Game version for in-game use. I.e. "ItemObject" is a scriptable object, and "GameItemObject" is the runtime variant. When creating the game version, JSON Utility can be used to clone the scriptable object data into the game version, which can then be changed and saved. I also use a repository object in the scene to reference the original Scriptable Object if needs be.
A word of caution: since ScriptableObjects retain values _after_ exiting play mode, this approach may lead to undoing previous work if you ain't careful enough. Otherwise, great tutorial. I'd personally attack the problem in an example from a different angle, but it does illustrate the point fairly well.
@@MattiaBelletti in this particular case, I'd go simple and just mark both player character's GO and UI manager go as DontDestroyOnLoad -- it's highly unlikely that the only data you'd need to retain about the player character is their health, after all. Also, I'd just make UI manager to FIndObjectOfType on awake to avoid making PlayerCharacter class being dependent on UI.
@@danielfazly1350 No need for that. Just create properties and assign them from serialized fields in OnEnable in your scriptable objects. This way you can separate runtime data or even fetch it from PlayerPreafs.
@@CockroachSlidy Can you explain that a bit more? So you have a sctiptable object which has default values, then in on on enable you get them from what serialized fields, from where a file?
that has nothing to do with "scriptable objects" , its just the correct event handling. one last advice: use x?.invoke() with the questionmark to prevent exception's in case the delegate x is empty.
This is really just moving the dependencies to something else that is not scene-dependent. It is quite useful though. The same could be achieved with C# events and I'm hoping the persistent side of scriptable objects isn't adding some overhead to these events. One thing you didn't mention though is since it is not scene dependent, it would be vital that you remove listener when your objects are destroyed or you will get exceptions.
i am doing something similar, but different in my RTS game. I am using scriptable objects to define Unit types. these can be created in the UI, and you can assign the values/sprites/etc to them. However I then have a Unit class, that has a UnitType property that is assigned the correct scriptable object. the Unit class does not alter any data in the ScriptableObject as that is being used as a Template for new instances of that type of unit. There are some helper methods I have in the UnitType to get the cost to craft the unit, its default health, damage, etc. I then have a singleton Unit manager class that handles spawning / tracking all of the units in the game, and also helps with saving/loading of the unit data. (The unit class is Serializable). I also have an independent script that is just for displaying details in the UI. So that concerns are kept separate from one another.
Although I've been using them already, you made me approach differently and optimize my project a lot! For example I have one scriptable object with a list of all my enemy behaviors and each enemy simply picks their properties from there. I can change only one asset now instead of 10s of prefabs
Personally I take it one step farther. First it's helpful to realize that the Unity editor IS a dependency injection framework. When you drag a SO onto a field in a MB you are setting up a dependency injection. Dependency injection and SO is not mutually exclusive. What I do it take it one extra step where there is a sort of registry for SOs based on a key. You can use strings, or whatever you like. I use string constants. Then, instead of hooking up the SO to the MB directly in the editor, you define what SO it needs via that key. At runtime it calls to the registry and asks for the SO by key and gets one returned. This mostly breaks the last hard dependency, leaving only the registry. It also allows you to build different registries if desired for testing or what not.
@@samyam Yeah that's an option, certainly viable. And you can even make different bundles for different things. It is however very heavy weight compared to a simple dispatcher where you just hook things up yourself for easy testing. Of course nothing says you can't use both options and even others.
This is a great example! Great use of ScriptableObjects. I absolutely love the note at the end on Dependency Injection. I would argue, if you use interfaces correctly there isn't really a need for ScriptableObjects this way. I prefer that method, but I can definitely see the usefulness of visibly seeing the data through the scriptable objects.
Awesome vid! You went through the concepts in a very digestable and clear way, adding some sweet graphics :) I think this approach really shines for making tech designer-friendly tools, which you mentioned at the end. If working with someone who can move around in Unity and iterate on mechanics and gameplay with these events without diving into code, it can be a huge productivity boost and fuel creativity while keeping the code decoupled. If just working with code, then I don't think it pays off compared to a regular event bus in C#.
There is a hidden important script code at 13:35! Having no references to other game objects is so difficult and at the same so important! In Unity is difficult to understand how references work inside a project. And if you hide or delete a game object with references that point to it you can get hidden bugs (null references) that do not pop up in the console.
Awesome video! Breaking everything down understandably for new coders! Beef it up by making a generic scriptable object event system and you have a totally decoupled code
Its my understanding that scriptable objects are best for storing read only data like item attributes and you don't want to be messing with changing values during runtime.
I don’t know why, but your videos are the only videos i don’t have to physically watch. I can listen to them and follow along easily enough! It might be the simple explanations and step by step process of how you think mixed with my general knowledge of unity.
Personally, I like to have my managers use a global static dependency injector. This works well for having a manager talk with it’s subsidiaries in scene though.
Well, great video regarding explanation and graphics, but I have strong opinion against this approach and scriptable object architecture in general. 1. While it can be designer friendly, it's error prone because you can forget to assign the event SO or someone can delete it. 2. As a programmer it's really too much work to jump from code to editor back and forth. 3. It creates the same problem as singleton bad example, you can't create more than one of these if you have multiple objects with Health logic. This is suitable for one player to many UIs, but it's not suitable for 100 units with 100 healthbars. Architecture imo should give you flexibility, but this approach requires separate Health handling for Player and other objects. I also think that bad examples to this approach comparison is quite unfair. There are much better ways to create health UI system without event SO approach. 1. UI which grabs reference to Player which has "Action/UnityEvent HealthChanged" is pretty fine. 2. Static EventBus which can be written in 20minutes is fine. 3. Simple static field in Player or somewhere else with Action/UnityEvent is fine. Last two give you same decoupling between Player and UI. I'm glad you covered dependency injection topic, but for me it's default way to go and I would encourage more people using it. It solves all dependency/decoupling problems and let you build proper architecture. Sure it requires a framework, but if you are really at this point when simple approaches, I suggested above, don't fit you project it's probably time to learn Zenject.
Greatest problem with being a software developer starting with Unity is the absence of IoC and Dependecy Injection. I´ve seen third party solutions, but this should be available out of the box.
I remember trying this after the talk you mentioned but wasn't there something weird that happens when you actually build a game into an exe. I can't remember it's been awhile but still a great video.
Great content, nice breakdown of the concepts. My only two cents are regarding the thumbnail; this trend of putting viewers down by telling them they're "wrong" and they must do things a "right" way is just silly. I've been seeing it more often lately and even when done in a jokingly way it just makes the community seem elitist.
It’s not meant literally or to bring anyone down, and this was actually the first time someone else made the thumbnail for me. Playing around with thumbnails to see what gathers more engagement. Sorry if it bothers anyone!
I've seen the unite videos, looked deeply into SOs, tried the Unity Atoms package, but all these ideas have made me formulate some thoughts. I've had a personal concern with using SOs as an event based medium. And it's that the project window could easily be muddled with seemingly hundreds of these objects, especially as a project grows, and no amount of folder organisation can seem to solve that problem. 🤔 But I'd be interested in a solution to this problem? It's a real problem that I've seen discussions about this before for, and is the main caveat that I could see happening when architecting events with SOs.
And to add to this, it's very easy for a designer to duplicate assets. So how could we know if something, somewhere in the entire project, is listening to, or broadcasting an asset? It's far too easy to delete assets that may have originally been thought to not be used anymore, and then the game is broken since it was in fact being used somewhere.
Something to alleviate this problem, is to have the assets use some form of editor scripting to detect if the asset is being referenced somewhere in the project. (List of broadcasters and listeners in the event's inspector) This would make tracking events drastically easier. I just haven't seen this done before, but would definitely make using these a whole lot easier.
@@Cameo221 i think these points were briefly touched in the unite video, where he stressed the necessity for planning debugging tools ahead (which to me includes stuff like "who is attached to this event or data in the project?", something an usual code editor is able to answer with a "show all references" tool).
I agree with mostly all that you had written. Scriptable objects are good as data containers. But you can use it as solution only when you have some infrastructure to use it. Events like most engineering problems, are half resolved. In this case the only way is to escape from project folder hell and drag n drop hell. Implement you tool UI to select scriptables (Like they do odin but you can use you custom editor solution). In my opinion the solution to this problems is to trash the project folder scriptable hell and not try to use scriptable as drag n droppable solutions. At least you can use selector on custom editor window etc..
Good video, but it left me feeling confused about one of your main points, which was the problem of having things coupled or dependent on other things. For example, at one point, you were talking about how if you wanted to test one Manager-class that had references to another Manager-class then you would have to include that other Manager-class in the test, because we need to have a reference to it. In order to fix that, you propose we use Events to fire off functions in other classes, as that is seen as a "decoupled approach". But it's not. In order to subscribe to an event in another class, we still need a hard-coded reference to that class. Sure, we could add some code to see if the reference exists or not, and if not, then nothing gets subscribed to. But that would remove our ability to test those functions, no? I've seen some games where an EventManager class is instantiated as a singleton, and then everything else kinda uses that as a "bridge" so that there is only one reference to maintain, and that helps cut down on the need for multiple references. But it still is not completely decoupled. Am I misunderstanding how this works?
Just wanted to validate your concerns Todd, you're absolutely right. There are no perfect solutions to the problem of coupling, the most we can really do is make it as hands-free as possible. Most of the time that means adding to the "execution context" whether that is editor references or singleton references. Taking the bigger issue here, this video is clearly just a new creator trying to build an audience. Every point comes directly from other sources, including the Unite 2016 talk and the Unity Open Source project called "Chop Chop!" - If you're genuinely trying to learn something I recommend going to the direct sources. A lot of context is lost in translation while this creator attempts to digest the information and put her own "content flair" on it for audience building purposes.
Fairly new to unity. Thank you so much for great videos so far. You give enough information for me to understand key concepts and it helps me tremendously whenever put it into context. Also you gloss over related concepts which is just amazing. One thing I was struggling with was instantiating health change event (cannot instantiate abstract class) keep up the great work!
I worked on many different projects from small to big. We weregetting disappointed if the game also gets bigger. You have toooooooooo many scriptableobjects everywhere. You need to search, click here and there to finally work with them. Sometimes you also do not know where it is used. The question often is there "Do I need it.. can I delete it?". Often you would need your own Editor Tool Solution to see what is where connected. It is great for designers but if you start to make easy for your designer, why not use visual scripting? This also makes problem with buttons. Did you ever say "oops" OnClick still tries to invoke function but actually you removed a button? In big projects this happens very often. Thats why it is better to have Button reference in the code instead doing it via the inspector. That's why we used other event system. We now can use same thing without scriptableobject and with coding only. For instance we can do RegisterEvent(OnDamage). Everybody who register Damage will receive the Damage object. This works everywhere and has not dependency. The goods is IDE can show all references and connected scripts. You can wrote listener if you want to just as drag and drop component. Still works for designers as well.
Hi, great video! You've pointed some important insights about SRP, decoupling and singletons, which is all great. The only thing that I actually dont agree is using SO as a middleware... you basically implemented a Publisher/Observer pattern but adding a SO, you could just make a simple Event Manager to handle messaging without it, the code would still be decoupled and you wouldn't have to make another SO for each corner case. Also what if multiple objects have to implement the same health logic? They cant just reference the same SO because they will always trigger this SO (e.g: you have character A and character B both referencing the Health SO. You have slide A and Slider B, both listening to Health SO. If character A decreases Health, both slider A and B would trigger) About persisting the data between scenes, I believe just serializing into json/binary would do the work
Hi! Sorry to bother but could you (or anyone reading this) elaborate on why is that usin SO as a middleman is unnecesary? I'm just getting started with this part of unity, but i'm not a begginer. I used Singletons in very basic proyects and now I kinda understand that they are bad when the project gets big. I started reading about unity events and how powerful they are, and now I encountered this SO based architecture in many vids that use SO as channels between triggers and listeners, that sounds powerful but I get the same vibe as you, is it really necessary to implement a channel for each communication? Doesn't it get messy? How would you implement a middleman that does what an SO does without it? Thanks for reading, and sorry for my bad english i'm a little rusty.
@@timothysdug5832 hi Tomás. The approach on the video is an abstraction to the Publisher/Observer pattern, which consists in decoupling entities that need to react to events in an application. Taking the example of the video, imagine that you have a Health object, and when the Health value is updated, different entities must react to it (e.g: the screen must shake if HP is subtracted , the UI must update the HP bar, the Death Logic must validate if the character is dead, etc.). In a highly coupled code, the HP script would manually call all these callbacks. This is bad because you delegate tons of responsabilities for a single class (HP), which means this class would have to know details of every class that should react to the HP Update, leading to a convoluted code and violating the Single Responsability Principle. The Observer pattern is a solution to ensure that a publisher (in this case the HP class) doesn't have to know every single observer (all the classes that must react to the HP update) and vice versa. In this pattern, the publisher just publishes a message/event "announcing" that something happened, and every listener subscribed to the message/event should react. The idea here is that none of these entities should know details of each other, the only thing connecting them is a Middleware that manages to assign publishers and observers. On the video approach, the middleware is a Scriptable Object, and I pointed on my first comment some of the problems in using SOs for that purpose instead of having a simple class managing the events. Of course that this is not written in stone, and there a multiple uses for SOs and for some cases it might be enough, but I personally wouldn't recommend it. If you're interested, this blog post contains a different approach to event handling that I really enjoyed and am currently using in personal projects: theliquidfire.com/2015/06/15/better-than-events/
@@chocobo678 I can't thank you enough for taking that time to write such a complete answer. You've given me a lot to think about and read. I couldn't find a straight answer to this issue anywhere. In the end i'll use both SO architecture and managers for events just to fully understand what each of them mean. Again, thanks a lot.
You're forgetting about how important it can be to make things in a designer friendly manner, therefore avoiding the need for changing any code. A designer can change these things by dragging SO files around. Also your mentioning of multiple instances being effected is easily fixable
To keep values over scenes you can simple use static values anywhere. The additive scenes should be more common practice, but is barely used. Delegates or Events should be used more often as well.
not to downplay statics, but you can only have one instance of a static variable. if you want to have a trap that damages foe and friend alike, you'd have to account for both a static variable for the player and the ones for enemies. or you'd have to consistently update the static variable through the player's health manager script
@@dmas7749 Once anyone takes damage, you of course need to update values, this process is best used with em delegates as mentioned, once you leave scene, to load new scene you update the static. There are no constant updates on statics needed. But it's all about structure. The best thing on static, it doesn't have to be in your scene as well.
@@dmas7749 As described in the above video, delegates are literaly events, any instance can subscribe to. Like a newsletter, if something happens, the message gets send to all listeners/subs and they can act with whatever they have subbed to the newsletter.
I can't believe how much trouble I'm having wrapping my head around these things. Around all of it really. I get it eventually but man, I don't know if my brain was made for this stuff.
Hey. Firstly, thank you for this detailed video. It's a lot of hard work and it's much appreciated. Just wanted to check, have you looked into using Visual Scripting Variables for the carrying data across scenes? I use it regularly and it's quite easy to setup and work with. p.s. - Not sure of the performance implications but it;'s definitely easy to set up.
@@samyam Awesome. Yeah would love your take on it. I was planning to record a really short video and put it on my channel as well. Looks like a lot of people haven't started using it and it might help someone!
You could also just make the slider dependant on the player which would not matter anywhere near as much. Because in what case is a level going to have the slider and not the player? Just make it on update get player health
UnityEvents add convenient functions that can be used. It all depends on what the problem you are trying to solve is. If you aren't seeing any negative effects from using UnityEvents then they can be quite useful.
Given this example, how would you handle several entities/gameObject who need their health managed? Would you have to create a new Health Manager SO for each entity?
Don't quite get it. For example, currently I'm using a script on the player object which does nothing but providing references to other player components so if anything wants to interact with the player components, it could get this script and reference whatever it needs from there. Should I replace it with scriptable object and references with events? If so, where should I get references to the object? Would it be fine (and not tedious) to manually put it from the project through inspector in every script which would interact with the player, like every enemy, door, NPC or whatever?
This is probably "bad to do" but I made my SaveManger a singleton this holds the player prefs and all I need to do when I save something is just set it in the instance save function, made using player prefs alot easier and seeing all I really store for saving and loading is world generation data & soon to be a list of objects+positions it's not dependent on anything but it's still something I'm very new with 🤔
So, to take this a little further, creating a "Character" SO to be a database for the health SO and other character related functions could work as a library without changing the actual Health SO? So many objects could be using the same Health or other stat SO. So basically the Database SO would I could make a List for the values of the attached SO's and that holds the values that are changed. It's getting confusing for me, I'd have to do this to see where it screws up XD. Thanks for the video, this looks like a lot of effort.
It's a very interesting video! Just one question: what's the pro of doing that instead of a classical event in the player script with the UI manager listening to it?
Hi samyam can we apply this scriptable object for more than one player. You know we have one player here but what if we have 3 or 4. They will have different health values. Will just one scriptable object is enough or should we create multiple scriptable object to change health of players seperately? By the way awasome video thanks.
I've seen the original video, but I still don't see how I can implement this architecture in a game where there is more than one playable object. Imagine I have a multiplayer game, with a variable number of players, maybe 2-8. And many, MANY enemies, at least hundreds of them. My characters have different properties, such as health, weapons, speed. Do I need to use SO for each character variant? But what if I need more than one copy of any character for enemies or players? Help me understand.
have a question, is there a reason not to make Singleton-type SOs static? because having a serializefield reference to the asset can be very annoying once you have tons of objects that get their event impulses from a single SO.
I've got a question for you about what happens if I need to have multiple gameObjects with the same component, such as health Component and I need to modify the values differently for a player or some enemies. With ScriptableObjects wouldn't have a health value for each one, in fact I would only one
Wasnt the decoupling part of the solution entirely due to using events? Couldnt the same decoupling be done with the singletoln pattern? Fire and forget health change events and if no singleton is listening, no worries. Im probably mising something. Interesting to hear about all the different approaches one could take
So would DI be good for external dependencies or loading databases/caching, then using scriptable objects to pass around data between those dependencies without creating tighter coupling? (Great Video!)
I mean can't we use DI to inject scriptable objects that already have the data availability/persistence deliverables filled by the di and updated by the game? Then move the data from SO's to db/cache using entities when persisting?
Hmm only on client side, it would still need to be serialized on serverside, then it would need to update the SO's on client side from the deserialized data. Interesting :)
I have a problem of accumulation time in a cooldown system... I make a cooldown system of 10 second to re spawn a new object useing a cooldown system... the problme when I got outside the play mode.. .the value changed... I mean is stored in the SO... sometimes can be ok, sometimes not.... the problem stast if I pass 90 secondes... I need to waitn 90 second for the next time to spawn a gabme object... for example , if I break the game at 10 second of playint time, I store 10 seconds, so the next time to spawn will be 20... the next one 30 , so far and so on.
I was wondering if this approach would still be worth doing, for enemies for example; wouldn't you still need to spawn lots of instances of the scriptable object along with their monobehaviours?
You wouldn't need this system for enemies. The main point here is the decoupling of UI from Player Health code. Enemies won't ever have this level of coupling between Health HUD elements. Enemy scripts can be designed independently from the player systems.
It depends. Scriptable objects are just assets. You could create a simple enemy game object that has its AI and other starting stats stored in a Scriptable object. Then you could have a MonoBehavior reference it. That way you can easily swap the enemy AI to see which type of enemy works best in that given level. Unite 2016 has great talk about this.
Not sure if you still read comments here, but I am curious why not we just use a static class to solve the issues or is there any other benefits that comes with this SO + event architecture that a static class cannot solve.
Nice one, I really like SO's, But i have a question. What if we have 100 enemies in our game, are we gonna create 100 SO's for every enemy? Especially they are getting spawned?
Hey, I think I can help you there. If they are all the same enemy with the same stats, every instance can just have a reference to the same SO. In this case, you would probably have a single enemy prefab with a script that has a reference to the SO and instantiate it 100 times. But if each enemy is supposed to have different stats (I don't think that is what you mean, though), you would probably need a SO for each one of them, yes.
No god no, please, nooo! I appreciate the effort you put into making the videos, but I hate the idea. I used it before, it looks fine on paper, but when the number of events increase significantly, the project gets messy real quick. Again, I do appreciate your effort, but this is a terrific way to use SOs. One reason is as I explained above, and the other reason is you will have hard time tracing your event calls and listeners and stuff, whereas for regular c# events, the IDE lets you literally just shift+f12 to find references to your events. No need to go to individual objects, figure out what references what, and messy stuff like that. Use SOs wisely please.
Thank you for your advice and input! I understand what you mean, there are pros and cons to different approaches, but I still think this is a valid way to organize projects. For a big project this method might create more overhead & make it harder to trace and debug, but for a smaller project it's very much worth exploring due to it's flexibility & organization.
A scriptable objet has a single reference to all game object in the scene, so what should i do if, for example, i have to create multiples enemies with the same base stats, but not the same actual health ?
Something that confuses me about this technique is; does this not create pretty tight coupling in of itself? If your player health lives inside an SO rather than the player itself, you run into the exact same scalability issue as a singleton does; it becomes incredibly difficult to change that SO significantly, you can't remove it if the health system changes, and you can't add another player without adding another SO and refactoring everything that depends on it. In essence, a SO kind of *is* a singleton - just one that doesn't live inside the scene. They're certainly one of the most powerful tools in Unity, but since watching that original talk this has always felt like a bit of an abuse of what they're intended for (That is, SO's are kind of meant to just hold persistent, usually constant data or logic that can be reused). If you took this approach to many things in a project, I just don't see how the amount of global state being managed wouldn't get out of hand. Some of these problems are solved by instantiating SO's at run time, as some people here have suggested. However, doing so you are then losing the core benefit of using the SO's in the first place, which is the ease of changing values. Just an observation on this method overall, super well made explanation and video nonetheless
I can't tell you how many times I've watched this video. So grateful you made such a beginner-friendly yet in-depth video. Thanks so much!!
😁
Another approach is a static class to hold actions that can then be invoked anywhere. That likewise doesn't have to sit on a game object of worry about being destroyed. But decoupled code can make it difficult to track event chains, since a publisher has no record of its subscribers. You have to use your search parameters in your code IDE to tracks event chains. Spaghetti is horrible, but so is a partially obscured event system.
Thank you, this did so much more for me than the Unite Austin talk. I could not ever wrap my mind around what he was saying, but this is just a really well-done demonstration.
Thank you! Appreciate the kind words 😁
I've seen the talk you're referencing ... and am very happy to found someone who goes and explains it in more detail! Thanks!!
singletons are actually used in games more than most other programming fields because the usual downsides of singleton (global state) are not as bad in video games since you know your code will be run in a single contained application. singletons cause issues in projects like web servers which are doing loads of things simultaneously and having a single shared resource and single shared piece of state can cause loads of problems, in a game its much easier to manage global state and singletons are a good way to keep global state from getting messy.
I'd also like to point out that your SOs are basically singletons implemented in a different way and have all the same issues with global state, plus some idiosyncracies with how SOs are implemented. The main advantage of this solution is you have avoided dependency chains, which is a problem that's already solved (most games will instantiate all their singletons at game boot time and set up dependency chains then)
Event based programming also has it's own set of problems and downsides. However this is a useful strategy I hadn't seen before so thank you for the informative video.
Thanks, your comment helped
There is a clear difference between non-Singleton SOs and Singletons [in the case of Unity, though I'm not sure what other game Engine also has SOs-- ]
Domain Reload.
Singletons require static referencing, so you must keep Domain Reload on to maintain a fresh state between Play Sessions in the Editor.
SO have no such requirement since they are always considered loaded within the Editor, and when they are referenced [by a MonoBehaviour] in a Build.
This difference means it is orders faster to test and iterate with non-Singleton SOs versus Singletons, especially as your project and assemblies grow larger.
Edit: I'm being explicit about non-Singleton SOs, because you can implement SO's as Singletons. They're not mutually exclusive.
Singletons are commonly used in web development, especially with frameworks that support hooks
I like to make a Game version for in-game use. I.e. "ItemObject" is a scriptable object, and "GameItemObject" is the runtime variant. When creating the game version, JSON Utility can be used to clone the scriptable object data into the game version, which can then be changed and saved.
I also use a repository object in the scene to reference the original Scriptable Object if needs be.
I remember watching your videos when you had less than 1k subs. So glad you stuck with it and are closing in on 20k. Keep up the good work.
Thanks so much for the support 😄
A word of caution: since ScriptableObjects retain values _after_ exiting play mode, this approach may lead to undoing previous work if you ain't careful enough. Otherwise, great tutorial. I'd personally attack the problem in an example from a different angle, but it does illustrate the point fairly well.
Yep, this is a common problem. Instead of altering the ScriptableObject, your scripts should make a new instance of it and alter that instead.
I'm interested in comparing various solutions for this kind of problems: which architecture would you put in place to solve it?
@@MattiaBelletti in this particular case, I'd go simple and just mark both player character's GO and UI manager go as DontDestroyOnLoad -- it's highly unlikely that the only data you'd need to retain about the player character is their health, after all.
Also, I'd just make UI manager to FIndObjectOfType on awake to avoid making PlayerCharacter class being dependent on UI.
@@danielfazly1350 No need for that. Just create properties and assign them from serialized fields in OnEnable in your scriptable objects. This way you can separate runtime data or even fetch it from PlayerPreafs.
@@CockroachSlidy Can you explain that a bit more? So you have a sctiptable object which has default values, then in on on enable you get them from what serialized fields, from where a file?
Your explanation is just nothing short of amazing!!!! So much love for this 🤗
Thank you!!!
that has nothing to do with "scriptable objects" , its just the correct event handling.
one last advice:
use x?.invoke() with the questionmark to prevent exception's in case the delegate x is empty.
*exceptions
The point here was to use SO as data storage over playerprefs or singletons. Event handling is just an example
This is really just moving the dependencies to something else that is not scene-dependent. It is quite useful though. The same could be achieved with C# events and I'm hoping the persistent side of scriptable objects isn't adding some overhead to these events. One thing you didn't mention though is since it is not scene dependent, it would be vital that you remove listener when your objects are destroyed or you will get exceptions.
i am doing something similar, but different in my RTS game. I am using scriptable objects to define Unit types. these can be created in the UI, and you can assign the values/sprites/etc to them.
However I then have a Unit class, that has a UnitType property that is assigned the correct scriptable object. the Unit class does not alter any data in the ScriptableObject as that is being used as a Template for new instances of that type of unit. There are some helper methods I have in the UnitType to get the cost to craft the unit, its default health, damage, etc.
I then have a singleton Unit manager class that handles spawning / tracking all of the units in the game, and also helps with saving/loading of the unit data. (The unit class is Serializable). I also have an independent script that is just for displaying details in the UI. So that concerns are kept separate from one another.
Although I've been using them already, you made me approach differently and optimize my project a lot! For example I have one scriptable object with a list of all my enemy behaviors and each enemy simply picks their properties from there. I can change only one asset now instead of 10s of prefabs
Awesome!
Thank you for this! I have to go back and change a lot of things for my personal project, but you did a great job showing me that will be worth it!
Personally I take it one step farther. First it's helpful to realize that the Unity editor IS a dependency injection framework. When you drag a SO onto a field in a MB you are setting up a dependency injection. Dependency injection and SO is not mutually exclusive. What I do it take it one extra step where there is a sort of registry for SOs based on a key. You can use strings, or whatever you like. I use string constants. Then, instead of hooking up the SO to the MB directly in the editor, you define what SO it needs via that key. At runtime it calls to the registry and asks for the SO by key and gets one returned.
This mostly breaks the last hard dependency, leaving only the registry. It also allows you to build different registries if desired for testing or what not.
You can also use addressables to load at runtime via name forum.unity.com/threads/loading-scriptable-objects-as-addressables.1540706/
@@samyam Yeah that's an option, certainly viable. And you can even make different bundles for different things. It is however very heavy weight compared to a simple dispatcher where you just hook things up yourself for easy testing. Of course nothing says you can't use both options and even others.
As my first introduction to scriptable objects, this was concise and extremely helpful. Looking forward to your other vids
This is a great example! Great use of ScriptableObjects.
I absolutely love the note at the end on Dependency Injection. I would argue, if you use interfaces correctly there isn't really a need for ScriptableObjects this way. I prefer that method, but I can definitely see the usefulness of visibly seeing the data through the scriptable objects.
As I search to scriptable Objects, you've put out a video helping me understand them better.
Many thanks Sam!
Glad to help! :)
Wonderful, when I was learning SOLID I didn't think it would be so useful in game dev.
Congratulations on your didactic
Awesome vid! You went through the concepts in a very digestable and clear way, adding some sweet graphics :)
I think this approach really shines for making tech designer-friendly tools, which you mentioned at the end. If working with someone who can move around in Unity and iterate on mechanics and gameplay with these events without diving into code, it can be a huge productivity boost and fuel creativity while keeping the code decoupled. If just working with code, then I don't think it pays off compared to a regular event bus in C#.
After starting to consistenly use this I can say that it changed my life
There is a hidden important script code at 13:35!
Having no references to other game objects is so difficult and at the same so important! In Unity is difficult to understand how references work inside a project. And if you hide or delete a game object with references that point to it you can get hidden bugs (null references) that do not pop up in the console.
Awesome video! Breaking everything down understandably for new coders! Beef it up by making a generic scriptable object event system and you have a totally decoupled code
Great idea for another video! Thanks 🙂
Happy to share code if you are interested? made the event system as a unity package for ease of sharing between our projects
Its my understanding that scriptable objects are best for storing read only data like item attributes and you don't want to be messing with changing values during runtime.
Your way of explanation is so simple and easy to understand. Even before watching, I somehow know that I will be able to digest all of the concepts!
That is my goal! Glad it is coming through 😄
I don’t know why, but your videos are the only videos i don’t have to physically watch. I can listen to them and follow along easily enough!
It might be the simple explanations and step by step process of how you think mixed with my general knowledge of unity.
Personally, I like to have my managers use a global static dependency injector. This works well for having a manager talk with it’s subsidiaries in scene though.
Well, great video regarding explanation and graphics, but I have strong opinion against this approach and scriptable object architecture in general.
1. While it can be designer friendly, it's error prone because you can forget to assign the event SO or someone can delete it.
2. As a programmer it's really too much work to jump from code to editor back and forth.
3. It creates the same problem as singleton bad example, you can't create more than one of these if you have multiple objects with Health logic. This is suitable for one player to many UIs, but it's not suitable for 100 units with 100 healthbars.
Architecture imo should give you flexibility, but this approach requires separate Health handling for Player and other objects.
I also think that bad examples to this approach comparison is quite unfair. There are much better ways to create health UI system without event SO approach.
1. UI which grabs reference to Player which has "Action/UnityEvent HealthChanged" is pretty fine.
2. Static EventBus which can be written in 20minutes is fine.
3. Simple static field in Player or somewhere else with Action/UnityEvent is fine.
Last two give you same decoupling between Player and UI.
I'm glad you covered dependency injection topic, but for me it's default way to go and I would encourage more people using it.
It solves all dependency/decoupling problems and let you build proper architecture.
Sure it requires a framework, but if you are really at this point when simple approaches, I suggested above, don't fit you project it's probably time to learn Zenject.
Greatest problem with being a software developer starting with Unity is the absence of IoC and Dependecy Injection. I´ve seen third party solutions, but this should be available out of the box.
OMG that's so awesome. Finally a solution without a bunch of annoying managers and singletons. Thank you very much :D
Very great tutorial, I like how you use bad examples first to explain what works and what doesn't.
Thanks Alex!! Hope it helped 😄
I'm glad you showed the problem with singletons. I know a lot if you tubers that still promote it.
Such an excellent video! I really love scriptable objects and want more people to get into using em 🙌🏽🔥
Thank you!!
Thank you so much. I was really hung about how to use events with non-global data, and I managed to implement it thanks to this video!
Glad it helped!
Very clear explanation of why you should avoid dependencies, thank you.
I remember trying this after the talk you mentioned but wasn't there something weird that happens when you actually build a game into an exe. I can't remember it's been awhile but still a great video.
Great content, nice breakdown of the concepts. My only two cents are regarding the thumbnail; this trend of putting viewers down by telling them they're "wrong" and they must do things a "right" way is just silly. I've been seeing it more often lately and even when done in a jokingly way it just makes the community seem elitist.
That's marketing brother. We don't have to like it, but it gets clicks.
It’s not meant literally or to bring anyone down, and this was actually the first time someone else made the thumbnail for me. Playing around with thumbnails to see what gathers more engagement. Sorry if it bothers anyone!
This is a great tutorial on scriptable objects. It guides you through both the why and the how.
I've seen the unite videos, looked deeply into SOs, tried the Unity Atoms package, but all these ideas have made me formulate some thoughts.
I've had a personal concern with using SOs as an event based medium. And it's that the project window could easily be muddled with seemingly hundreds of these objects, especially as a project grows, and no amount of folder organisation can seem to solve that problem. 🤔
But I'd be interested in a solution to this problem? It's a real problem that I've seen discussions about this before for, and is the main caveat that I could see happening when architecting events with SOs.
And to add to this, it's very easy for a designer to duplicate assets. So how could we know if something, somewhere in the entire project, is listening to, or broadcasting an asset? It's far too easy to delete assets that may have originally been thought to not be used anymore, and then the game is broken since it was in fact being used somewhere.
Something to alleviate this problem, is to have the assets use some form of editor scripting to detect if the asset is being referenced somewhere in the project. (List of broadcasters and listeners in the event's inspector) This would make tracking events drastically easier. I just haven't seen this done before, but would definitely make using these a whole lot easier.
@@Cameo221 i think these points were briefly touched in the unite video, where he stressed the necessity for planning debugging tools ahead (which to me includes stuff like "who is attached to this event or data in the project?", something an usual code editor is able to answer with a "show all references" tool).
@@MattiaBelletti Certainly. I would definitely use this as long as it can track references, just like in code 🙂
I agree with mostly all that you had written. Scriptable objects are good as data containers. But you can use it as solution only when you have some infrastructure to use it. Events like most engineering problems, are half resolved. In this case the only way is to escape from project folder hell and drag n drop hell. Implement you tool UI to select scriptables (Like they do odin but you can use you custom editor solution).
In my opinion the solution to this problems is to trash the project folder scriptable hell and not try to use scriptable as drag n droppable solutions. At least you can use selector on custom editor window etc..
Good video, but it left me feeling confused about one of your main points, which was the problem of having things coupled or dependent on other things. For example, at one point, you were talking about how if you wanted to test one Manager-class that had references to another Manager-class then you would have to include that other Manager-class in the test, because we need to have a reference to it. In order to fix that, you propose we use Events to fire off functions in other classes, as that is seen as a "decoupled approach". But it's not. In order to subscribe to an event in another class, we still need a hard-coded reference to that class. Sure, we could add some code to see if the reference exists or not, and if not, then nothing gets subscribed to. But that would remove our ability to test those functions, no? I've seen some games where an EventManager class is instantiated as a singleton, and then everything else kinda uses that as a "bridge" so that there is only one reference to maintain, and that helps cut down on the need for multiple references. But it still is not completely decoupled. Am I misunderstanding how this works?
Just wanted to validate your concerns Todd, you're absolutely right. There are no perfect solutions to the problem of coupling, the most we can really do is make it as hands-free as possible. Most of the time that means adding to the "execution context" whether that is editor references or singleton references.
Taking the bigger issue here, this video is clearly just a new creator trying to build an audience. Every point comes directly from other sources, including the Unite 2016 talk and the Unity Open Source project called "Chop Chop!" - If you're genuinely trying to learn something I recommend going to the direct sources. A lot of context is lost in translation while this creator attempts to digest the information and put her own "content flair" on it for audience building purposes.
Thank you very much, your video has saved me a lot of time. C# is a new language for me.
Fairly new to unity. Thank you so much for great videos so far. You give enough information for me to understand key concepts and it helps me tremendously whenever put it into context. Also you gloss over related concepts which is just amazing. One thing I was struggling with was instantiating health change event (cannot instantiate abstract class)
keep up the great work!
Thanks so much!
Did you find a sollution to the abstract class thing? I'm thinking it's because i'm not on the correct version of unity
I come ti watch her video not only because of the tutorial, but also enjoyable voice
Thank you :)
I worked on many different projects from small to big. We weregetting disappointed if the game also gets bigger. You have toooooooooo many scriptableobjects everywhere. You need to search, click here and there to finally work with them. Sometimes you also do not know where it is used. The question often is there "Do I need it.. can I delete it?". Often you would need your own Editor Tool Solution to see what is where connected. It is great for designers but if you start to make easy for your designer, why not use visual scripting?
This also makes problem with buttons. Did you ever say "oops" OnClick still tries to invoke function but actually you removed a button? In big projects this happens very often. Thats why it is better to have Button reference in the code instead doing it via the inspector.
That's why we used other event system. We now can use same thing without scriptableobject and with coding only. For instance we can do RegisterEvent(OnDamage). Everybody who register Damage will receive the Damage object. This works everywhere and has not dependency.
The goods is IDE can show all references and connected scripts. You can wrote listener if you want to just as drag and drop component. Still works for designers as well.
Hi, great video! You've pointed some important insights about SRP, decoupling and singletons, which is all great. The only thing that I actually dont agree is using SO as a middleware... you basically implemented a Publisher/Observer pattern but adding a SO, you could just make a simple Event Manager to handle messaging without it, the code would still be decoupled and you wouldn't have to make another SO for each corner case. Also what if multiple objects have to implement the same health logic? They cant just reference the same SO because they will always trigger this SO (e.g: you have character A and character B both referencing the Health SO. You have slide A and Slider B, both listening to Health SO. If character A decreases Health, both slider A and B would trigger)
About persisting the data between scenes, I believe just serializing into json/binary would do the work
Hi! Sorry to bother but could you (or anyone reading this) elaborate on why is that usin SO as a middleman is unnecesary? I'm just getting started with this part of unity, but i'm not a begginer. I used Singletons in very basic proyects and now I kinda understand that they are bad when the project gets big. I started reading about unity events and how powerful they are, and now I encountered this SO based architecture in many vids that use SO as channels between triggers and listeners, that sounds powerful but I get the same vibe as you, is it really necessary to implement a channel for each communication? Doesn't it get messy? How would you implement a middleman that does what an SO does without it?
Thanks for reading, and sorry for my bad english i'm a little rusty.
@@timothysdug5832 hi Tomás. The approach on the video is an abstraction to the Publisher/Observer pattern, which consists in decoupling entities that need to react to events in an application.
Taking the example of the video, imagine that you have a Health object, and when the Health value is updated, different entities must react to it (e.g: the screen must shake if HP is subtracted , the UI must update the HP bar, the Death Logic must validate if the character is dead, etc.). In a highly coupled code, the HP script would manually call all these callbacks. This is bad because you delegate tons of responsabilities for a single class (HP), which means this class would have to know details of every class that should react to the HP Update, leading to a convoluted code and violating the Single Responsability Principle.
The Observer pattern is a solution to ensure that a publisher (in this case the HP class) doesn't have to know every single observer (all the classes that must react to the HP update) and vice versa. In this pattern, the publisher just publishes a message/event "announcing" that something happened, and every listener subscribed to the message/event should react. The idea here is that none of these entities should know details of each other, the only thing connecting them is a Middleware that manages to assign publishers and observers.
On the video approach, the middleware is a Scriptable Object, and I pointed on my first comment some of the problems in using SOs for that purpose instead of having a simple class managing the events.
Of course that this is not written in stone, and there a multiple uses for SOs and for some cases it might be enough, but I personally wouldn't recommend it. If you're interested, this blog post contains a different approach to event handling that I really enjoyed and am currently using in personal projects:
theliquidfire.com/2015/06/15/better-than-events/
@@chocobo678 I can't thank you enough for taking that time to write such a complete answer. You've given me a lot to think about and read. I couldn't find a straight answer to this issue anywhere.
In the end i'll use both SO architecture and managers for events just to fully understand what each of them mean.
Again, thanks a lot.
You're forgetting about how important it can be to make things in a designer friendly manner, therefore avoiding the need for changing any code.
A designer can change these things by dragging SO files around.
Also your mentioning of multiple instances being effected is easily fixable
8:52 ScrawtVermillion the Melty Blood GOAT
Clearly explained. Well done.
To keep values over scenes you can simple use static values anywhere. The additive scenes should be more common practice, but is barely used. Delegates or Events should be used more often as well.
not to downplay statics, but you can only have one instance of a static variable. if you want to have a trap that damages foe and friend alike, you'd have to account for both a static variable for the player and the ones for enemies. or you'd have to consistently update the static variable through the player's health manager script
@@dmas7749 Once anyone takes damage, you of course need to update values, this process is best used with em delegates as mentioned, once you leave scene, to load new scene you update the static. There are no constant updates on statics needed. But it's all about structure. The best thing on static, it doesn't have to be in your scene as well.
@@OctoManGames my understanding of delegates is pretty shit, what all do you know about them?
@@dmas7749 As described in the above video, delegates are literaly events, any instance can subscribe to. Like a newsletter, if something happens, the message gets send to all listeners/subs and they can act with whatever they have subbed to the newsletter.
Ha!!! All 3 bad examples can be found in my current project. Thank you for the great content.
😂 Well every project is different and maybe another way makes more sense! But for larger projects and teams SO’s are a great option 👌
Great video.. Really liked the example problem you stated before and giving solutions to it!
Thank you!!
This is sooooo useful! Thanks!
Wish to see more contents like this.
my app is a GUI so this really helps a lot, thank you!!
I can't believe how much trouble I'm having wrapping my head around these things. Around all of it really. I get it eventually but man, I don't know if my brain was made for this stuff.
Just keep going and making projects, it’s all about experience and practice; you’ll soon be an expert!!
Godot signals: "look what they have to do to mimic a fraction of our power"
Seen your channel newly. Youre amazing
ooh.. can't wait...
This is so helpful! Thanks :)
Awesome video as always! I love scriptable objects and use them for everything I can 🤣
Thank you!! 😄
Hey. Firstly, thank you for this detailed video. It's a lot of hard work and it's much appreciated.
Just wanted to check, have you looked into using Visual Scripting Variables for the carrying data across scenes? I use it regularly and it's quite easy to setup and work with.
p.s. - Not sure of the performance implications but it;'s definitely easy to set up.
Thank you! No I haven’t looked into it, wasn’t aware of it. I’ll take a look when I have time!
@@samyam Awesome. Yeah would love your take on it. I was planning to record a really short video and put it on my channel as well. Looks like a lot of people haven't started using it and it might help someone!
You could also just make the slider dependant on the player which would not matter anywhere near as much. Because in what case is a level going to have the slider and not the player? Just make it on update get player health
thank you for such a beginner friendly tutorial... I have work to do... 😅
This video as good as ryan hipple's talk. I immediately subscribed! Keep up the good work and thanks for the video :)
Thanks so much!! 🙂
Is there a reason to use UnityEvents instead of regular c# events?
UnityEvents add convenient functions that can be used. It all depends on what the problem you are trying to solve is. If you aren't seeing any negative effects from using UnityEvents then they can be quite useful.
Given this example, how would you handle several entities/gameObject who need their health managed? Would you have to create a new Health Manager SO for each entity?
i think you should just create a healthsystem class and make an instance of it for each entity
I use dont destroy on load. For stuff like that i use delegates actions and unityevents. When you call them check for null event?.Invoke
()
Great video. Thanks for the insight.
Was this channel hide or something? How I didnt know about it? Amazing
Don't quite get it. For example, currently I'm using a script on the player object which does nothing but providing references to other player components so if anything wants to interact with the player components, it could get this script and reference whatever it needs from there. Should I replace it with scriptable object and references with events? If so, where should I get references to the object? Would it be fine (and not tedious) to manually put it from the project through inspector in every script which would interact with the player, like every enemy, door, NPC or whatever?
It's interesting, although I think that the leg work is done by the observer pattern.
In your example of an event subscription "middle-man", what would be the advantage of a Scriptable Object instead of a public static class?
Can this solution scale with multiple instances of Player? Seems like it might be limited to just one player?
This is probably "bad to do" but I made my SaveManger a singleton this holds the player prefs and all I need to do when I save something is just set it in the instance save function, made using player prefs alot easier and seeing all I really store for saving and loading is world generation data & soon to be a list of objects+positions it's not dependent on anything but it's still something I'm very new with 🤔
So, to take this a little further, creating a "Character" SO to be a database for the health SO and other character related functions could work as a library without changing the actual Health SO? So many objects could be using the same Health or other stat SO.
So basically the Database SO would I could make a List for the values of the attached SO's and that holds the values that are changed. It's getting confusing for me, I'd have to do this to see where it screws up XD. Thanks for the video, this looks like a lot of effort.
It's a very interesting video! Just one question: what's the pro of doing that instead of a classical event in the player script with the UI manager listening to it?
Really helpful videos, thanks!
Thank you :)
why do it this way when c# has the event delegate pattern? the event keyword is quite powerful
Hi samyam can we apply this scriptable object for more than one player. You know we have one player here but what if we have 3 or 4. They will have different health values. Will just one scriptable object is enough or should we create multiple scriptable object to change health of players seperately? By the way awasome video thanks.
I've seen the original video, but I still don't see how I can implement this architecture in a game where there is more than one playable object. Imagine I have a multiplayer game, with a variable number of players, maybe 2-8. And many, MANY enemies, at least hundreds of them. My characters have different properties, such as health, weapons, speed. Do I need to use SO for each character variant? But what if I need more than one copy of any character for enemies or players?
Help me understand.
Thanks, for your tutorial sis
How is the scriptable object pattern more de-coupled than the singleton pattern? Seems identical
This is magic, thanks!
Excellent tutorial!
Thanks so much!!
have a question, is there a reason not to make Singleton-type SOs static? because having a serializefield reference to the asset can be very annoying once you have tons of objects that get their event impulses from a single SO.
I've got a question for you about what happens if I need to have multiple gameObjects with the same component, such as health Component and I need to modify the values differently for a player or some enemies. With ScriptableObjects wouldn't have a health value for each one, in fact I would only one
Wasnt the decoupling part of the solution entirely due to using events? Couldnt the same decoupling be done with the singletoln pattern? Fire and forget health change events and if no singleton is listening, no worries. Im probably mising something. Interesting to hear about all the different approaches one could take
So would DI be good for external dependencies or loading databases/caching, then using scriptable objects to pass around data between those dependencies without creating tighter coupling? (Great Video!)
I mean can't we use DI to inject scriptable objects that already have the data availability/persistence deliverables filled by the di and updated by the game? Then move the data from SO's to db/cache using entities when persisting?
Hmm only on client side, it would still need to be serialized on serverside, then it would need to update the SO's on client side from the deserialized data. Interesting :)
I have a problem of accumulation time in a cooldown system... I make a cooldown system of 10 second to re spawn a new object useing a cooldown system... the problme when I got outside the play mode.. .the value changed... I mean is stored in the SO... sometimes can be ok, sometimes not.... the problem stast if I pass 90 secondes... I need to waitn 90 second for the next time to spawn a gabme object... for example , if I break the game at 10 second of playint time, I store 10 seconds, so the next time to spawn will be 20... the next one 30 , so far and so on.
I was wondering if this approach would still be worth doing, for enemies for example; wouldn't you still need to spawn lots of instances of the scriptable object along with their monobehaviours?
You wouldn't need this system for enemies. The main point here is the decoupling of UI from Player Health code. Enemies won't ever have this level of coupling between Health HUD elements. Enemy scripts can be designed independently from the player systems.
It depends. Scriptable objects are just assets. You could create a simple enemy game object that has its AI and other starting stats stored in a Scriptable object. Then you could have a MonoBehavior reference it. That way you can easily swap the enemy AI to see which type of enemy works best in that given level. Unite 2016 has great talk about this.
@@akitoakito still doesnt answer the question about +100 enemies
Why use UnityEvent and add a NonSerialized attribute? You can just use Action or am I wrong?
I'm wondering if it the scriptable object approach is still worth using if none of its data/events have to be used outside of one scene
Not sure if you still read comments here, but I am curious why not we just use a static class to solve the issues or is there any other benefits that comes with this SO + event architecture that a static class cannot solve.
A youtuber sponsored by another youtuber? How the turn tables.
Nice one, I really like SO's, But i have a question. What if we have 100 enemies in our game, are we gonna create 100 SO's for every enemy? Especially they are getting spawned?
Hey, I think I can help you there. If they are all the same enemy with the same stats, every instance can just have a reference to the same SO. In this case, you would probably have a single enemy prefab with a script that has a reference to the SO and instantiate it 100 times. But if each enemy is supposed to have different stats (I don't think that is what you mean, though), you would probably need a SO for each one of them, yes.
No god no, please, nooo! I appreciate the effort you put into making the videos, but I hate the idea. I used it before, it looks fine on paper, but when the number of events increase significantly, the project gets messy real quick. Again, I do appreciate your effort, but this is a terrific way to use SOs. One reason is as I explained above, and the other reason is you will have hard time tracing your event calls and listeners and stuff, whereas for regular c# events, the IDE lets you literally just shift+f12 to find references to your events. No need to go to individual objects, figure out what references what, and messy stuff like that. Use SOs wisely please.
Thank you for your advice and input! I understand what you mean, there are pros and cons to different approaches, but I still think this is a valid way to organize projects. For a big project this method might create more overhead & make it harder to trace and debug, but for a smaller project it's very much worth exploring due to it's flexibility & organization.
The video is great, i'm just having a little trouble implementing the scriptable objects in more complex mechanics. =/
Could you not use the scriptable object implementation on the health manager class (in a separate scene)?
That's great, but about Depdency Injection, is there a way to initialize objects with correct references in code rather than drag&drop ?
The drag drop method IS dependency injection.
A scriptable objet has a single reference to all game object in the scene, so what should i do if, for example, i have to create multiples enemies with the same base stats, but not the same actual health ?
Instances. Or make life easy and use Unity Atoms
Aren't the scriptable objects writable only in editor?
Something that confuses me about this technique is; does this not create pretty tight coupling in of itself?
If your player health lives inside an SO rather than the player itself, you run into the exact same scalability issue as a singleton does; it becomes incredibly difficult to change that SO significantly, you can't remove it if the health system changes, and you can't add another player without adding another SO and refactoring everything that depends on it. In essence, a SO kind of *is* a singleton - just one that doesn't live inside the scene.
They're certainly one of the most powerful tools in Unity, but since watching that original talk this has always felt like a bit of an abuse of what they're intended for (That is, SO's are kind of meant to just hold persistent, usually constant data or logic that can be reused). If you took this approach to many things in a project, I just don't see how the amount of global state being managed wouldn't get out of hand.
Some of these problems are solved by instantiating SO's at run time, as some people here have suggested. However, doing so you are then losing the core benefit of using the SO's in the first place, which is the ease of changing values.
Just an observation on this method overall, super well made explanation and video nonetheless