Stop Using Singletons With Runtime Set ScriptableObjects (Unity Tutorial)

In this Unity ScriptableObject tutorial we will explore the concept of Runtime Sets introduced by Ryan Hipple of Schell Games in his Unity 2017 talk Game Architecture with Scriptable Objects. We'll look at how can use ScriptableObject in Unity3d to replace singleton patterns in your game, avoid race conditions and spaghetti code.
This is a part of our Scriptable Cookbook series on the channel so if you're interested to learn more about Unity Scriptable Objects check out the playlist here: • Scriptable Cookbook: S...
You can download the full source code shown in this video here: bit.ly/scriptablecookbook
Check out Ryan Hipple's source from his talk on Github for more examples: github.com/roboryantron/Unite...

Пікірлер: 68

  • @razzraziel
    @razzraziel4 жыл бұрын

    There is a minor bug at 6:10 You're just pushing that int to nowhere and always returning first item on list (items[0]) So it should be return items[index];

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Yeah thanks, I saw that after I posted it 😅

  • @JasonWelch
    @JasonWelch Жыл бұрын

    I'm probably using an anti-pattern, but I have a single SO called "Blackboard" which holds references to every important runtime object such as the player, the various managers, etc. This is then referenced by practically everything. Each of these managers, the player, etc. when spawned or enabled/awake, automatically set the references to themselves on the blackboard. I may eventually break this up into discrete SOs that contain references to specific groups of objects such as managers, but for now, it does the job well enough. Objects such as items automatically add and remove themselves from their respective managers which I've recently decided to convert to mono behaviors because each manager lives in the "GamePlay" scene which is always loaded along side the other scenes, and so items in the non game play scene are able to get the reference via the blackboard. By having zone / scene specific object instances automatically add and remove themselves, I can simply create prefabs and drop them in whatever scene I want without any further configuration.

  • @gasparweb
    @gasparweb4 жыл бұрын

    Great video! (This and the other on the ScriptableObjects Cookbook). After some days of watching videos on the subject, I must say this is one of the best. Please keep doing this kind of videos: spot-on, no series based, dynamic and fun!

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thank you Gaspar! Yes I will definitely continue exploring this concept. Thanks!

  • @gasparweb

    @gasparweb

    4 жыл бұрын

    @@newbquesttv sorry to bother, can't find this example on the repo. Just the enums ones are there, but not the Runtime Sets!

  • @erz3030
    @erz30304 жыл бұрын

    This is such an important topic. You explain things so well, especially with the brief side-tangent examples/explanations as you do, it helps to elucidate this 'beyond beginner' concept. You have an excellent ability to come at topics in a very direct and usable way, from this one, and a couple of your previous videos I've seen(procedural tree and buildings/city generation). I really hope you continue making more videos, and in this same style as well. Cheers!

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thanks so much for the kind words Eric! I really appreciate it :)

  • @JasonStorey
    @JasonStorey4 жыл бұрын

    I like the idea, though personally I would be inclined to jazz it up a little with a signal pattern. rather than dragging in a reference to the scriptable object, you could have the "AddGameObjectRuntimeSet" dispatch a Signals.Dispatch(this); and have a single scene instance scriptable binder that has the SO field assign in inspect, listen to that and perform the assignment, that way you can have one place in the scene to maintain which scriptable is the runtime set. you could even filter on GetType and drop them into separate sets. It also might be out of scope for this video as a tutorial, but I would add a custom indexer to the runtime set collection type so you can streamline the contract to playerSet[0].transform . great video.

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thanks yeah that’d be interesting to try. I haven’t worked much with signal patterns before. Appreciate the thoughtful comment!

  • @Songfugel

    @Songfugel

    4 жыл бұрын

    Isn't this basically a view-model that you are describing?

  • @jdonaldsontcs
    @jdonaldsontcs3 жыл бұрын

    This was a great tutorial! Definitely some great knowledge to have working with Unity.

  • @newbquesttv

    @newbquesttv

    3 жыл бұрын

    Thank you! Appreciate your checking it out.

  • @SunnyValleyStudio
    @SunnyValleyStudio2 жыл бұрын

    Thanks a lot for this vid! I didn't understand the concept behind this (and I don't think that you have mentioned it explicitly) but basically with SO now our EnemyManager lives as .asset data so we can create a prefab out of the enemy and it will persist the connection to the SO while if we have a MonoBehaviour we would need to create / find it each time in our scene. Thanks!

  • @soerennielsen
    @soerennielsen4 жыл бұрын

    Hm, this is interesting. Definitely gonna try and play around with this, but as you said, it's not a "one and only solution to everything" and I'm not super fond of the kinda hard-coded GetItem with and index. But it should be able to work around things like that. I do like the flexibility and the way dependencies will be resolved. Great video, keep it up Matt!

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thanks man! Yeah I think it could be improved for sure. This is more of just me pointing to the idea and opening a conversation. I definitely am going to experiment further myself as well. Thanks for checking it out!

  • @CodingWithUnity
    @CodingWithUnity4 жыл бұрын

    Great video, very well explained

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thank you!

  • @irql2
    @irql24 жыл бұрын

    You have a new subscriber! I found your videos while looking for a damage system for a little game I want to make. I was about to write my own but thought something as common as a Damage System has to be a solved problem and there would probably be tons of examples out there, right? There are and the more I look in to it, the more I think I do have to write my own because there are so many different ways to implement such a system. I may spend more time customizing someone elses vs writing my own.

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Glad to hear you found it useful thanks!

  • @TheJonBrant
    @TheJonBrant4 жыл бұрын

    This looks really useful. I've had some bad times with the race conditions you mentioned. Too many 'bool isInitialized' used and it's ugly. This seems like it would take some getting used to, but a useful trick. Thanks!

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Yeah exactly, in this case you know that things are there because they’re in the project. Give it a try and let me know how it goes!

  • @TheJonBrant

    @TheJonBrant

    4 жыл бұрын

    @@newbquesttv Considering starting yet another project that will likely remain unfinished, seems like a good candidate. Will do. Also, thought you might be interested in this: kzread.info/dash/bejne/oKGcrpVroJS4YM4.html

  • @dracovarius
    @dracovarius4 жыл бұрын

    Hello Matt, I like seeing alternate uses for ScriptableObjects like this one. I hadn't encountered it before so thank you for showing it so clearly. I do wonder how this compares to, for example, the FSM of PlayMaker. In an FSM you can also register game objects to variables of that FSM. Does that suffer from the same order of operation issues as you described in the video? Keep up the great content!

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thanks, glad you found it clear! I wasn’t sure if I was jumping around too much :) I think playmaker and it’s use of assets does maybe have some similarities but I’ve only ever used it for tiny demos so can’t really speak too authoritatively on it. Glad you found the video interesting!

  • @CaptainJeoy
    @CaptainJeoy4 жыл бұрын

    Another important use I think one could check out, is a modular scriptable object ways to check for collisions between 2 objects. I've been casually thinking about ways via Scriptable Object without having to disturb the other colliding object in the monobehaviour OnCollisionEnter() and co, callback functions.

  • @CaptainJeoy

    @CaptainJeoy

    4 жыл бұрын

    Even with my adoption of a Modular Game Architecturing pattern via Scriptable Object, GameObjects still find themselves crossing each other's paths because of collision l, I just wish everything can be fully modular and during collision, every check and collision info is handled in Scriptable Objects

  • @muratcanagic
    @muratcanagic2 жыл бұрын

    i can do whatever i want sir!

  • @GuillaumeKehren
    @GuillaumeKehren4 жыл бұрын

    Good job ;)

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thanks 😊

  • @chriswatts3697
    @chriswatts36974 жыл бұрын

    Well i tried to setup my enviroments a bit like this. Most of the time i just added a script dynamically with Component Add to the Instantiated GO and that script did most of the "spcial operations" like finding something etc. I used a lot of simple structures like Lists or Dictionaries to store information of GOs like what kind of enemy one GO is or stuff like that. Do`t know if that is an effective approach...

  • @videogamedevelopmentseries2706
    @videogamedevelopmentseries27062 жыл бұрын

    This method is working great but my nav mesh agents seem to be losing the parent transform somewhere along the travel. They get close to the player transform but then so slightly beyond it and miss the trigger area I have set for them to setActice(false); Something I'm missing I think. Do I need to update the nav mesh at run time also?

  • @DylanBennett
    @DylanBennett4 жыл бұрын

    You mentioned at the end how this solves the problem of things being tied together (in the example of making a demo scene). But this setup seems like you'd run into a problem if you dropped an enemy into a scene with no player. In the enemy's Start(), wouldn't it get a null reference from the playerSet and then fail on every Update() due to a null reference exception?

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Hey yeah that’s fair, I think my example here may not be perfect. Ideally the enemies wouldn’t be hard coded to chase the player from the beginning of the scene and have null checks and stuff. It’s an interesting question though of whether the player qualifies as a kind of singleton too with some of the same problems. I think it’s kind of rare you’d have scenes without any players but it’s possible. 🤔

  • @darkman237
    @darkman2373 жыл бұрын

    Would you please do an inventory/item system tutorial that utilizes these concepts?

  • @traywor1615
    @traywor1615 Жыл бұрын

    I read: Stop Using Singletons And Runtime Set ScriptableObjects (Unity Tutorial) Notice the and? Yes, I was pretty confused (as I have heard the talk earlier)

  • @AnthelmeDumont
    @AnthelmeDumont4 жыл бұрын

    I tend to not use scriptable object for this particular case, but more static classes with static methods, it's removing the need of passing scriptable object reference via the editor. Did you use both together sometimes and in which case?

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Broadly I’d say what this helps to replace or provides an alternative to is the approach you describe with static singletons, like a game manager or enemy manager. I recommend checking our Ryan Hipple’s talk in the ScriptableCoolbook playlist for more context.

  • @internetexplorer7880
    @internetexplorer78802 жыл бұрын

    "Reads title" Me: what?

  • @albertmontagutcasero2129
    @albertmontagutcasero21294 жыл бұрын

    Hey, this is working in android? When I compile the game, I get all references to scriptable objects as null.

  • @MrHandsy

    @MrHandsy

    3 жыл бұрын

    You have to either have it referenced some some scene (aka scene reference baking) or instantiate it before somehow referencing it.

  • @TheLuka102
    @TheLuka1024 жыл бұрын

    Does this still work when you build your game? As far as I know saving data to Scriptable Objects works only in the editor, but I've seen similar solutions multiple times now. I'm just confused.

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    To actually save data persistently you'll need to serialize it to something like JSON. ScriptableObjects are better for passing references or for creating edit time data. You don't really want to write persistent data to them.

  • @mikoajkubiak7775

    @mikoajkubiak7775

    3 жыл бұрын

    @@newbquesttv The question is I think, would code in this video work in build. Cause SO don't seem like they are changeable in build. You cannot change their values in runtime, once in build. So no adding nor removing from the list.

  • @jefflynch

    @jefflynch

    3 жыл бұрын

    @@mikoajkubiak7775 Yes, it will work in a build. What won't work in the build is the serializing/deserializing of the data contained in the Scriptable Object. By this I mean that in the Unity Editor, we are accustomed to changing the values of variables on SOs either in the Inspector or via script, and they will be continue to persist even after you stop playing, unlike Monobehaviors. The persistence (the values being saved and carried over from play session to play session) is what won't work in a build. The values will be reset to the values that were set when you did the build. However, you can use a SO just like any other C# object - including modifiyng its values, in a build. The values will reset after you stop playing, but that's exactly the behavior you want for a runtime set. All that happens is they lose their ability to carry over values between play sessions (because in a build there is no Editor saving their state).

  • @r1pfake521
    @r1pfake5214 жыл бұрын

    You can avoid your "race condition" problem by using the execution order attribute and give the singleton or managers (or whatever you want call them) a lower number (or even negative number) this assures that they will always be initialized first.

  • @BlackkTiger

    @BlackkTiger

    3 жыл бұрын

    Many people consider this to be poor practice, as it's really easy to break things later on and not remember/realize why. If you move your code to a new project, it won't work unless you remember to set up the execution order the same way. Similarly, if you change the execution order later for some unrelated reason, you could end up breaking these things that rely on it. You're also relying on Unity not screwing with this feature later on. To the greatest extent possible, your code shouldn't depend on project settings to work.

  • @bluzenkk

    @bluzenkk

    Жыл бұрын

    no, dont do that, avoid doing that at all cost. Run away fast (racing). cuz down the road, you will have trouble remembering which and why you put the execution order that way when the project gets big...

  • @r1pfake521

    @r1pfake521

    Жыл бұрын

    Well obviously it shouldn't be overused. If you have to run your init logic in a specific order then you should only use this attribute on one class and this one class should initialize all other classes in the correct order and not add the attribute to all other classes, but in most cases you can avoid the "race" issue by properly splitting the logic between Awake and Start and / or use a class which handles the order of the initialization of other objects If you need to initialize your objects in a specific order then this is the "safest" way, because Awake / Start can run in a different order during editor / build, so don't write your code in a way that depends on the logic that the Start method of object A is called before the Start method of object B, just because it is called in that order in the editor, trust me this kind of code will cause you way more issues than the attribute with a explicit order. This is also the reason why some Unity classes use this attribute, because they want to assure that the order is correct and not "hope" that it will run in the correct order. - "you will forget why" IF you use this attribute then you should obviously add a comment to the attribute and write down why you need it and why this specific custom value as picked. If you have big projects you will have some documents which describe important details like this anyways, in addition to the comment in the specific class - "If you change it later, you can break things", I don't see the point here, why would you randomly change the value? If you change something important like that then you must have a good reason and review why you change it and to which number you change it - "Unity will break it". Not sure if you knew this but many of the Unity classes use this attribute and it has been there for many years, so they will not randomly break it. Or the change of breaking it is about the same as they could break anything else, but in this case it's easy to test if it works or not - "will not work if you move your code to a new project" by using the attribute the logic is independend of project settings, so yes it will work if you simply copy the code, but again, this attribute should only be used in rare cases. So hopefully you don't even need the code in the new project because you learned your lesson and set it up in a better way - "your code shouldn't depend on project settings" again, the attribute is part of the code, not a project setting, but overall a project can have many settings that have to be moved over if you want to have the same behvior in a new project for example tags, layers, physic settings etc. Btw singletones and global managers are also considered poor practice yet many Unity devs use them. I personally don't use them, but every project is different and requires different "tricks". This comment was only meant to make people aware that the attribute exists, I never stated that they should overuse it for every thing or that it is the best solution. Becaus adding the attribute is easier than refactoring your whole system, depending on how early you notice the "issue" ;)

  • @timsem2624
    @timsem26244 жыл бұрын

    I saw, somewhere in youtube video how one guy had connected scriptable object like event machine, with less of code. What do you think about it? Matt.

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Yeah I am going to do a video on scriptableobject events soon, just need to do some more research first

  • @umapessoa6051
    @umapessoa60512 жыл бұрын

    To be honest you're creating a lot of other problems while trying to solve another one...

  • @TheNamesJT
    @TheNamesJT4 жыл бұрын

    Umm, have you thought of making a 2d game series where your viewers can follow like a simple game while using this pattern?

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    I don’t really like doing linked series since people usually drop off pretty fast by second episode. That said something small and 2D that explores this in a focused way could be cool!

  • @TheNamesJT

    @TheNamesJT

    4 жыл бұрын

    @@newbquesttv Yeah, that would be nice :) will be waiting hehe

  • @BrainSlugs83
    @BrainSlugs834 жыл бұрын

    @6:13 Ummm you might want to take a closer look at your code there because that is definitely not what it's doing...

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    yup, it's a bug, see the pinned comment

  • @behnamrasooli8801
    @behnamrasooli88014 жыл бұрын

    You are reinventing a dependency injection framework. It might be easier to just use an existing solution like Extenject and get familiar with this kind of architecture. Rolling your own DI framework takes some years of experience which obviously you don't have. Sorry, I'm too harsh, but you talk so confidently as if you know what you're doing. This is dangerous and misleads people.

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    Thanks for the feedback but if you watch my content I'm pretty clear that I'm not putting this out as "this is the architecture everyone should use" but instead "this is a tool and pattern I'm learning and experimenting with". If you have an approach you prefer or would like to see a different type of content I would love to see what you yourself would make. The magic of KZread is that we all can make whatever content we want to share with the world, and I think the world is a better place with more people learning and sharing. I don't think there's any world where making free programming tutorials is "dangerous" even if they're wildly or even subtly wrong.

  • @behnamrasooli8801

    @behnamrasooli8801

    4 жыл бұрын

    @@newbquesttv You're right. It's true that we all can make whatever we want and share it. I just left my opinion here so that people know there is a better way. This approach brings accidental complexity to the project. This is the magic of comment-able content ;)

  • @newbquesttv

    @newbquesttv

    4 жыл бұрын

    @@behnamrasooli8801 yup, understood. Thanks!