I agree. I tried implementing a more designer-centric development process in our process at a previous company I worked at years ago, but I never had the time to develop it to the point of discovering the capabilities of ScriptableObjects. UnityEvent was a Godsend, and a worthy successor to SendMessage, which had potential, but some serious caveats and performance issues.
Totally agree. Far to many videos are purely about how to make X work. That's fine, but when you approach game dev a little more professionally, these architecture decisions/challenges comes up pretty fast - and are very hard to find the "best practice" for often. Personally I come from a coding background, so I'm not a super fan of all this drag'n'drop stuff. Sure of cause the editor is awesome to build 3d worlds etc, but like references between things very quickly becomes a nightmare. Yes you can do "Find references in scene"....but what if you have multiple scenes? Generally figuring out how things are referencing each other is one of my big challenges - as I want things to be a simple as possible. Too often, if not always, it's based on "something you need to know" - and that sucks :) Haven't watched this video to the end yet, hoping for some ideas - however I am already running a lot of stuff "event" driven, via a Message system in code, so I'm on the same path.
36:25 "If you write a simple inspector" This is kinda just thrown out there. For people wondering where this button came from, paste this code in a new C# Script using UnityEngine; using UnityEditor; [CustomEditor(typeof(GameEvent))] public class WhateverYourScriptNameIs : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); GameEvent myGameEvent = (GameEvent)target; if (GUILayout.Button("Raise()")) { myGameEvent.Raise(); } } }
So I took this concept and ran with it a bit. Here are some things I learned and some advice for other developers: All my ScriptableObjects inherit from a base class ScriptableGameObject, that has a few fields on it, most notably a string for a developer description. This is never actually used but it basically means any developer on the project can write anything they want about any object in the inspector. Also, It's probably best to also make a ConstFloatReference, which is the same as a FloatReference (or whatever) but doesn't let you change the value in your code (you can still set a custom value in the inspector and can still reference a normal variable). This has no bearing on the actual game and there's no such thing as a "constant" in your actual variable classes, but it means that at the reference point, you can't accidentally change the value of a variable in your code. This is useful if, for example, you have UI elements that read the players health but you want to make sure that they cannot change the players health under any circumstances, so it's effectively a "one-way" variable link. It's just an extra level of safety at the cost of a little bit of extra work. I also have a generic ScriptableObject-based ObjectPool which takes a prefab and can automatically handle creation/enabling/disabling etc of objects in the pool. This is extremely useful because it encourages me to use more object pools (I can create them basically as I need them with a simple right click, and create, say, a "Pistol Bullet Pool") which itself results in a more efficient game because every weapon that fires projectiles can very easily use an object pool and even share them (in the case of multiplayer). Lastly, if you combine this with pluggable stuff like Pluggable AI as seen in the other Unity videos on this topic, you can get a lot of flexibility. In general you should be favoring references over a complex inheritence heirarchy or a lot of singletons. But singletons are still required, unfortunately. For example, it's pretty much impossible to set a game state without using a singleton. I mean you can technically use a game state variable, but if you want all of your game objects to, say, not move when the game state is "Paused", you either have to use a singleton to store the gamestate so you can check it, or you have to reference a game state variable in each object, which can be an absolute pain because you will likely have to do it with every prefab in your entire project.
I was wondering about your second point. It would seem without that everything would be just be made into a global which is really dumb. For your reference variable, is it just a wrapper essentially for the real variable and just returning the value? That seems like it would be the smartest thing you can do to make sure that not everything can write to everything.
@@litgamesdev The reference wraps the variable, but also contains a value of type T which can be used instead, selectable from the inspector (it also contains a bool to say if it's using the reference or not). This allows developers to easily have "one-off" cases. Nothing here is global.
Thanks for your help :). Your last point about needing to use a singleton, what if you are just activating everything that the camera sees and loading in chunks then it might not be that much effort to pause all the objects in sceen using a scriptable object event.?
@@pixelpat5276 my general rule is: if it can be done without a Singleton, it probably should be. If you have a system to work with your camera that makes it self sufficient with minimal dependencies, then you're golden. The reason singletons are bad in the first place is because they add hidden dependencies to your objects and if that can be avoided, then it's probably a good thing.
Same here... Holy shinning cubes! Scriptable objects just went from "meh" to "amazing!" in my mind. I was so not understanding their advantages! I'm shocked at how deep I was down in Dunning-Kruger effect...
actually, we have something similar in tv production animations with assets constantly referenced in various animation shots. and any change in the assets gets an update throughout. Talk about updating all the 200 shots just for more skin texture switch.
I'm sitting here stunned, realizing how many stupid simple problems this would have solved for me if I had watched this back in nov 2017 as first posted... I've used scriptable objects for more typical data, but using them as globally available "meet in the middle" kind of user editable data is so obvious now!
2:38 Engineering Pillers 7:04 ScriptableObjects Basics 15:26 Modular Data 27:50 Event Architecture 38:30 Runtime Sets 45:32 No More Enums 50:33 Asset Base Systems
This is an amazing demonstration of the power of ScriptableObjects. I spent a week re-watching this video, and modifying the demo scenes, until I attained complete understanding. It illustrates I've been overlooking the power of using ScriptableObjects, as opposed to singletons, in my work. I especially like your descriptions of what the 'wrong singleton alternatives' are - you perfectly pointed the path down which I went, not knowing what Unity was capable of. Thank you for sharing this excellent work. I can tell you hid company secrets, as you hinted at in the demonstration - yet you pointed out all the salient points of ScriptableObjects quite well anyway. Replacing the GameEvent listener list with Delegates is an exercise to be done by the student, for extra credit. ;-)
This really is a great talk, as you pointed out, we have lots of info on how to accomplish very specific things in Unity, but I feel that design best practices / design patterns specifically within Unity is one of the areas I have the hardest time getting information about.
@32:41 lmao he actually moved the background art thing up to perfectly not-cover the word "hookers." Obviously great presentation overall but I specifically appreciate the effort that went into this easter egg of a meme
@@herohiralal3255 i think this is what he said: You don't reference the "owner" at all to get the value because the SO holds the value. It's better and safer for designer because it gives them a GUI to create their own values with other SO features to help. Changes affect all scenes and any objects which rely on them(stops accidental prefab changes and is a form of serialization). That's what I got.
Awesome talk, thank you! I personally get rid of using Editor to set references as much as possible as it's really difficult yo find those references later or make sure Unity did not broke them for unknown reason at some point. Doing everything via code saved my life but I will give a try to use scriptable object based event system.
I cant express how important the video is ... THis is one of the best video in UNITE, THE demonstrator is super knowledgeable and has shown powers of SOs, and is really good at demonstration. THank you very much
I started using these for pretty much everything. They solve pretty much every issue I typically have to deal with. I never wanted to use multiple scenes because it was a pain to serialize and deserialize data between scenes. Now it doesn’t matter. I can just drop in whatever I want without a care in the world.
This is very useful, its a shame there isn't people like him (that I'm aware of) making unity courses or books that go through how to apply this stuff in detail, I'd find it very useful to have a book/course that goes through how to apply this step by step rather than an overview.
I have just discovered this video and I have to say that it is amazing! For the first time, someone has explained me very well how ScriptableObjects can be used
While eye-opening, this presentation sent me down a two-day rabbit hole which consisted of me slamming headfirst into scripting mania and error headaches. Making float variables was the easy part. I can barely customize the editor to display them in the Inspector!
I agree, he was very easy to listen to, not condescending in the least, intelligent, articulate. I would like to see more game development topics presented by him.
I'm so glad someone directed me to this talk. Had no idea how powerful Scriptable Objects could be, and will definitely be watching the Richard Fine and The Hack Spectrum talks next. Thanks Ryan!
Been doing Unity at home for a couple years now and always knew I should be using Enums. I used them for the first time last night, spent a bunch of time setting up a bunch of stuff using them ... then added a new one near the top of the list ... OMG !! ... it didn't keep the values I set where I used them !! ... well THATS good to know !! ... and here I am :)
This is fascinating. In a lot of ways, Unity and its particular setup seems to get in the way when you try to program from a typical perspective. I'm very new to it, so I'm not fully on board with its style of doing things yet. You've created some very clever ways of doing things that embrace Unity's strengths and that's pretty awesome. This and Richard Fine's talk about ScriptableObject should be required viewing. Thank you very much! :-)
Thanks for this great talk, especially for the demos and use cases presented; still very useful in 2018. :) And exactly what I was looking for, because as a Unity noob (but someone who understands SOLID programming in .Net) I had a lot of dilemmas about how to implement loosely coupled design in Unity, and most sources don't answer this at all. I also appreciate the critical tone towards some Unity characteristics, e.g. the curly brace placement and relying on magic strings.
Wish we had an example of that inventory system, saving/loading and resetting those scriptable objects must be quite tricky. Not sure if I want to attempt that myself yet.
This was an incredibly useful speech that hit many issues that I struggled with. Virtually every point I have worked at some point but was unsatisfied with my solutions. After watching this it's time to take another whack at it. Thanks!
For anyone interested - we've just open-sourced our implementation of this concept, called SmartData. It's comprehensive, powerful, extensible, includes custom editors and even a live node-graph visualizer/debugger. github.com/sigtrapgames/SmartData
So to make sure that I've got this architecture concept straight, I'd like to use scriptableobjects for my multiplayer game. For this reason, I'd like the shared configuration of this type - things like Max Health etc., to be FloatVariable, IntVariable, and so on as required (Or simply - BaseConfiguration to hold all of them together, increased locality). In order to let our UI bind itself to the player's data, I'd like to use FloatReference, IntReference, etc (Or simply - PawnStateReference - holds everything you intend other systems to interact with, leaving other fields as true private fields for encapsulation) Then, the player our UI recognizes will have PawnStateReference hold the PawnStateVariable SO, and other clients of the exact same type/prefab will use the "constant" (isn't runtime a better name?) instead. Simple, client and other players share the exact same components with the player having an additional Input script that handles converting the inputs to actions via client pawn. UI only knows what our client pawn is doing extensibly, and if I'd like to add UI elements per every other player, I can use a RuntimeSet of all pawns in the scene and access the PawnStateReference from there, as it'll be public. I don't even need to add a messy "IsClient" boolean or whatever because I could just compare the PawnState reference with the one my UI recognizes. Is this considered an attempt to overengineer scriptableobjects into my game or does this seem like proper usage of the proposed architecture? Thanks.
i didn't know until now that scriptable objects are important for game architecture. I always wondered how Developers manage to do this in AAA Games. Really eye opening talk. Recommended Talk!!!!
I'm a very new programmer. I started learning C# & Unity Development in late July of 2020 (it is late September 2020 as of writing this.) This talk has helped me out so much, that I cannot even describe the sheer thankfulness, and happiness I have from it. I'm currently at that point where I fully understand C# on a fundamental level. I'm familiar with the dynamic of value types & reference types on an intuitive level, and I am also familiar with all data types and how to properly implement them in accordance to eachother. The problem I do have though- what is the most efficient manner to use all of these tools together, in order to create the cleanest, most efficient, and modular manner? I really really like the system that he presented here, and I am going to be using it from here on out. This is a fantastic way to create systems that are decoupled from eachother. Utilizing the data types are the shared resource between different systems is a take that I have never even considered doing. I always thought that you HAD to reference system to one another, in order to create any sort of communicative functionality between said systems.
I really like the modularity and designer-centric focus of this approach. I use Zenject now and like it, but it's often not quite as easy to pivot with zero programmer involvement.
So basically variables that may need to be shared between different "systems" could/should be individual SO's so it's easy to do so. That's pretty cool!
This is one of the most amazing talks I have seen.... Demonstrating stuff as you say about it... Yayyy to Scriptable objects.... Btw that we are not monsters line was XD
I tested it and combined it with pluggable approach. this architecture really challenges other architectures. Authorable for non-coders and easy to maintain
this looks great and the talk is huge and very well done, It's just really hard for beginners to understand how to correctly implement them, or this is how I'm feeling
I like the idea of using an asset as a source of publicly communicating some data. It reminds me of a Unix Socket in the unix/linux world, a sort of public channel for reading/writing data.
This is super nice talk. I've used this architecture in a couple of games and found myself often re-writing the same boilerplate code (for events), so I've found a really nice open-source library for it: github(dot)com/chark/scriptable-events. I really recommend using it!
This talk, and the 2016 talk by Richard Fine are huge eye-openers, and greatly appreciated! I was wondering though: would it be a good idea to make the GameEvent and EventListener classes generic? That way, it can be parameterized for other generated UnityEvent types.
Great talk! One downside that wasn't mentioned is that the event binding is only visible in the inspector, which makes this approach a little more unfriendly when viewing the source code outside of the Unity IDE.
This is a re-upload of a previous version of this video that had audio sync issues. They also could've been there in person or maybe they're even a co-worker or friend that helped edit or critique the presentation. Why would you put someone on blast just for saying, "Good Job"?
If I'm making an rpg with X amount of characters, and each of them has an inventory. If I tried to use this pattern, would I just create one inventory for each character? Or is that a case where Scriptable Objects stops being a good idea
This kind of depends on a few more factors. ARe the number of characters set? Is this a case like Final Fantasy where all your possible party members are named and pre-defined? Or is it more an RPG where you create your own party members, and it's an arbitrary number? The more things are static and known in advance, the more useful it is to use SOs for this. The more dynamic, the less useful. But even then, you can still instantiate SOs at runtime and set their properties as normal. At the end of the day, they're just normal C# classes.
I have some strong doubts about this: - How do you manage execution order? Let's say, when my player dies, I want some object to react first, then other objects. - Your whole code is tied to the Unity engine, which means it's a lot harder, if not impossible, to switch to another engine that does not function the same way. In my opinion, you should only use Unity as a view, and have all of your code logic and data somewhere else. - How do you manage large number of entities? As far as I know, Unity Objects can only be accessed on the main thread, and Unity Events generate quite a lot of garbage when you have big amount of entities. - You're basically writing something quite similar to visual scripting, except you don't have the benefits of visual scripting, with the nice flow visualization and debug options. - How do you quickly edit data, when it's all over the place in many different scriptables? You have to create a huge editor window to edit everything? - When in a smaller team, if you don't have a real QA department, how do you do code reviews? I tend to think the other way around, anything that is pure code can be easily reviewed, while scenes, gameobjects and components are quite annoying to track. However, I have to admit there's a lot of advantages as well, especially that system dependency thing which seems quite nice to work with.
I can't answer these in depth, but one thing I have found that works very well is implementing interfaces in your core codebase with scriptableobjects. This way, you aren't inherently tied to Unity for obvious reasons and can still get all the benefits of using scriptable objects. I do the same thing with monobehaviours as well.
It's certainly an interesting approach and it plays to the strength of using Unity as a tool for building the game in-engine versus in your IDE. You pointed out some valid criticisms, I think it really depends on what the team needs designers to do and what the artist/designer/programmer ratio is.
Looks like great stuff but I failed to understand some details (I am a novice in Unity). I wish there were more examples and code, less common phrases. Could anyone explain how FloatReferences are supposed to be used when we have not a single player with specific HP, but a bunch of different instances of units with their own individual HPs (like in RTS)? Are these FloatReferences HP have to be created for each unit at runtime? The same question is for Events: how it would be if an event could be raised by multiple sources (I don't want it to be received by all subscribers, but only those related to this specific source/instance)? Should we create a separate scriptable object for every event source? Could anyone provide some links to more complicated examples?
I feel you man, I tried to replicate his examples in a little project having a player and multiple enemies. I tried using his UnitHealth and just putting it on the enemy but as you said it would reference the same health the player references. I'm now Instantiating the health SO at runtime but finding it difficult to get a reference for that from the health bar. I would love for someone with experience to chime in with some wisdom.
I'm late but I would have the enemy have a MaxHealth FloatReference and a private float for currentHealth inside it that it tracks by itself. You only need to use this stuff for DI (Dependency Injection) anyway. Idk tho I've been a dirty Singleton user all my life so
If you are planning to have a constant number of human players in a given game session, ever say 4-8 then this approach would still work. If you are referring to X number of dynamic enemies that could be more than the 10+ and that comes and go at any given time then I would avoid this approach as you’ll end up creating X number of HP instance per enemy, and that’s only HP we’re talking and not counting other properties. However let’s say if each enemy had their own little HP UI above them, I suppose you could dynamically create an instance of the float variable with ScriptableObject.creatInstance() and wire that onto the enemy’s UI at runtime.
Great talk, web lacks architecture advice for Unity developers. I wonder how they handle version source control issues using this approach. There are many changes after each game start, so it should be some type of runtime folder that must be in .gitignore file. But in that case game either will not start right after clone game repository or scriptable objects should be created by themself.
You reintroduce the tight coupling problem that singletons bring though. And ScriptableObjects are accessible from everywhere anyway. They're assets, so you can reference them like any other asset from within another class as normal. Like referencing a texture or an icon.
He explained at 22:33 how his FloatVariable scriptable object could be used as a parameter in the animator but he never actually showed how to do it. How do you actually bind the two? Do you actually have to do somewhere in an 'Update' method a `animator.SetFloat(myFloatVariable)` to keep it in sync? What's the trick here?
You may have meant at 22:10? He says "I'd like to see" as it's not possible. One good spot for that `Update` would be a StateMachineBehaviour that has a field for a FloatVariable. This would allow syncing the animator parameter to match the FloatVariable only while in animator nodes that rely on that parameter for a transition.
The biggest drawback really is the fact that you can't easily trace changes and diffs in source control. I understand the advantages of larger teams to delegate a lot of things to the editor when you have a lot of non-programmers. But otherwise that's really a pain point.
Working with enums isn't an issue with serialization, because you ALWAYS define the value for the enum to avoid these issues. But if some loss of performance isn't an issue, one can use the alternative approaches instead.
Yeah, I wasn't entirely sure why they couldn't safely remove certain enum values without breaking things. The compiler should be telling where enums that no longer exist are being used through errors.
One more observation - the notion that DI is exactly what the inspector is for and that there's no need for DI frameworks like Zenject is only partially true imo. It would make more sense if: A. Every single class in your project that requires dependencies is either a MonoBehaviour or a ScriptableObject B. You don't use interfaces However in reality, you would want to use basic C# classes and interfaces and you can't inject their dependencies via inspector. Zenject also has many useful features like binding factories and memory pools. I would advocate for a combination of the inspector and a DI framework.
Interfaces can still work with the inspector! You serialize a reference to the UnityEngine.Object and use validation (OnValidate, ISerializationCallbackReciever, CustomEditor, PropertyDrawer) to reject the Object if it does not implement the interface. Then on Initialization (or ISerializationCallbackReciever.OnAfterDeserialize) you can cast to your interface and store it.
It sounds very hacky. I'm sure there are many ways to make it work. You could also purchase Odin Inspector from the Asset Store and serialize pretty much anything. But even if point B wasn't valid, there's still point A. I'm of the opinion that you shouldn't pollute scenes with objects that aren't 'actual things' in the scene, but are just there to run MonoBehaviours. The point is that the inspector cannot fully replace a DI framework.
This method requires that any consumers of a variable that are only concerned with changes in the value have to constantly poll the value and compare it to a previous state. This could have huge performance implications
Not if you make a variable object invoke a delegate on change! Some use cases benefit from checking every frame for data that changes a lot. Others can just get an event when it changes.
Great talk, Ryan! You mention that the demo version of StringReference is a lot simpler than the one you use in production. I have been working on a very similar concept that is generic and free to use. I created a forum post for this, as well. As of yet, no one seems interested, so I would love some feedback from you (or anyone for that matter)! For anyone else interested in this, head to the forums to share your thoughts and questions. Or, better yet, start to contribute on the BitBucket repository! BitBucket Link: bitbucket.org/nobrainerinteractive/referenceablevalue Unity Forum Post: forum.unity.com/threads/referenceablevalue.505389/ Cheers, David
Thank god he doesn't use a Mac. Don't know why that always bothers me so much. Also, funny how they think people won't notice he is using an MSI laptop. Anywho, great talk. Definitely one of the most informative and insightful
I'm a game artist looking at this guy's forehead and I think I just unlocked the ability to visualize better shading in my head now. Thankyou Ryan Hipple.
What if we had a class, like "ApplicationInitializer" which makes all the singleton classes according to what we want, (like DemoLevelManager or ReleaseLevelManager, both can be LevelManager.Instance) and the singleton works as a way to reach those that have common function. All LevelManagers have to implement or handle, for example, LoadLevel() or invoke LevelFinished, or have their own distinct way of having LevelGoalTracker. In this way the singleton is still modular because we are injecting the singletons in ApplicationInitializer, and no race condition because we are in control of initialization order in ApplicationInitializer. Edit: Yea, Dependency Injection. But then how would we easily access the info we need? Every Enemy class has a reference to EnemyManager? What if an unrelated code wants to go through all enemies, it would want to reach EnemyManager.
Awesome talk, I really like the floatReference idea, but there is something I struggle to understand. How can I use float reference for runtime instance of object? Like the buildings health in your RTS exemple. We can put all the SO in runtime sets or dictionnary but it seem to be a "hacky" solution. And I can't figure how runtime sets and events can resolve totally the need of singleton or static reference, if I want the room where the player is standing or if I want to call a function of my saveSystem with parameters the singleton seem to be better.
Correct, SO was never intended to be a one stop solution for all. Understanding and combining SO usage where appropriate along with singleton just gives you an upper hand when making decisions in architecture. However, If you don’t want the singleton approach and want a more robust gameManager for larger scale games, then you can look into using a Dependency Injection framework.
You make a MonoBehaviour called Scope. Inside the Scope are dictionaries where SOs are keys. Values of the dictionaries can either be local copies of the SOs, real values (like float, bool, int, etc) for a little better performance an less memory, or a cacheable index of a list that has the real values for the best performance. Inside your other code, you call a method like Scope.GetFloat(SOFloat). Then, what GetFloat does is try to use SOFloat in a dictionary to get the value to return and, if it doesn't find it, an entry is created, initialized, and returned. PD For the cacheable index of a list. You'd cache the index inside a FloatReference instance, you'd pass that FloatReference to GetFloat instead of an SOFloat, and you only get the index from the dictionary if the SO referenced by FloatReference has changed or if it's being used with a new Scope. That way, getting the scoped value, most of the time, just means getting a value at an index from a list which can be a lot faster.
This was 5 years ago and yet, I still come back to this video for reference. This changed the way I use ScriptableObjects forever.
Very good talk, we need more talks in this quality for Architecture (y)
I agree. I tried implementing a more designer-centric development process in our process at a previous company I worked at years ago, but I never had the time to develop it to the point of discovering the capabilities of ScriptableObjects. UnityEvent was a Godsend, and a worthy successor to SendMessage, which had potential, but some serious caveats and performance issues.
Totally agree. Far to many videos are purely about how to make X work. That's fine, but when you approach game dev a little more professionally, these architecture decisions/challenges comes up pretty fast - and are very hard to find the "best practice" for often.
Personally I come from a coding background, so I'm not a super fan of all this drag'n'drop stuff. Sure of cause the editor is awesome to build 3d worlds etc, but like references between things very quickly becomes a nightmare. Yes you can do "Find references in scene"....but what if you have multiple scenes?
Generally figuring out how things are referencing each other is one of my big challenges - as I want things to be a simple as possible. Too often, if not always, it's based on "something you need to know" - and that sucks :)
Haven't watched this video to the end yet, hoping for some ideas - however I am already running a lot of stuff "event" driven, via a Message system in code, so I'm on the same path.
36:25 "If you write a simple inspector"
This is kinda just thrown out there. For people wondering where this button came from, paste this code in a new C# Script
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(GameEvent))]
public class WhateverYourScriptNameIs : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
GameEvent myGameEvent = (GameEvent)target;
if (GUILayout.Button("Raise()"))
{
myGameEvent.Raise();
}
}
}
Thank you!
So I took this concept and ran with it a bit. Here are some things I learned and some advice for other developers:
All my ScriptableObjects inherit from a base class ScriptableGameObject, that has a few fields on it, most notably a string for a developer description. This is never actually used but it basically means any developer on the project can write anything they want about any object in the inspector.
Also, It's probably best to also make a ConstFloatReference, which is the same as a FloatReference (or whatever) but doesn't let you change the value in your code (you can still set a custom value in the inspector and can still reference a normal variable). This has no bearing on the actual game and there's no such thing as a "constant" in your actual variable classes, but it means that at the reference point, you can't accidentally change the value of a variable in your code. This is useful if, for example, you have UI elements that read the players health but you want to make sure that they cannot change the players health under any circumstances, so it's effectively a "one-way" variable link. It's just an extra level of safety at the cost of a little bit of extra work.
I also have a generic ScriptableObject-based ObjectPool which takes a prefab and can automatically handle creation/enabling/disabling etc of objects in the pool. This is extremely useful because it encourages me to use more object pools (I can create them basically as I need them with a simple right click, and create, say, a "Pistol Bullet Pool") which itself results in a more efficient game because every weapon that fires projectiles can very easily use an object pool and even share them (in the case of multiplayer).
Lastly, if you combine this with pluggable stuff like Pluggable AI as seen in the other Unity videos on this topic, you can get a lot of flexibility. In general you should be favoring references over a complex inheritence heirarchy or a lot of singletons. But singletons are still required, unfortunately. For example, it's pretty much impossible to set a game state without using a singleton. I mean you can technically use a game state variable, but if you want all of your game objects to, say, not move when the game state is "Paused", you either have to use a singleton to store the gamestate so you can check it, or you have to reference a game state variable in each object, which can be an absolute pain because you will likely have to do it with every prefab in your entire project.
Hey Paul, thanks! This is super helpful!
I was wondering about your second point. It would seem without that everything would be just be made into a global which is really dumb. For your reference variable, is it just a wrapper essentially for the real variable and just returning the value? That seems like it would be the smartest thing you can do to make sure that not everything can write to everything.
@@litgamesdev The reference wraps the variable, but also contains a value of type T which can be used instead, selectable from the inspector (it also contains a bool to say if it's using the reference or not). This allows developers to easily have "one-off" cases.
Nothing here is global.
Thanks for your help :). Your last point about needing to use a singleton, what if you are just activating everything that the camera sees and loading in chunks then it might not be that much effort to pause all the objects in sceen using a scriptable object event.?
@@pixelpat5276 my general rule is: if it can be done without a Singleton, it probably should be. If you have a system to work with your camera that makes it self sufficient with minimal dependencies, then you're golden. The reason singletons are bad in the first place is because they add hidden dependencies to your objects and if that can be avoided, then it's probably a good thing.
You know, I wasn't entirely sold on scriptable objects until this talk. Thanks, it has been rather eye opening.
Same here... Holy shinning cubes! Scriptable objects just went from "meh" to "amazing!" in my mind. I was so not understanding their advantages! I'm shocked at how deep I was down in Dunning-Kruger effect...
actually, we have something similar in tv production animations with assets constantly referenced in various animation shots. and any change in the assets gets an update throughout. Talk about updating all the 200 shots just for more skin texture switch.
That's exactly what I went through, that's probably why he was shitting on Richard, cause his talk was meh at best but this is brilliant
I'm sitting here stunned, realizing how many stupid simple problems this would have solved for me if I had watched this back in nov 2017 as first posted... I've used scriptable objects for more typical data, but using them as globally available "meet in the middle" kind of user editable data is so obvious now!
Without a doubt the most mind blowing Unity talk ever. Excellent speaker.
he is rather good, I have to say
2:38 Engineering Pillers
7:04 ScriptableObjects Basics
15:26 Modular Data
27:50 Event Architecture
38:30 Runtime Sets
45:32 No More Enums
50:33 Asset Base Systems
Holy sh{}t
This is an amazing demonstration of the power of ScriptableObjects. I spent a week re-watching this video, and modifying the demo scenes, until I attained complete understanding. It illustrates I've been overlooking the power of using ScriptableObjects, as opposed to singletons, in my work. I especially like your descriptions of what the 'wrong singleton alternatives' are - you perfectly pointed the path down which I went, not knowing what Unity was capable of.
Thank you for sharing this excellent work. I can tell you hid company secrets, as you hinted at in the demonstration - yet you pointed out all the salient points of ScriptableObjects quite well anyway. Replacing the GameEvent listener list with Delegates is an exercise to be done by the student, for extra credit. ;-)
This really is a great talk, as you pointed out, we have lots of info on how to accomplish very specific things in Unity, but I feel that design best practices / design patterns specifically within Unity is one of the areas I have the hardest time getting information about.
@32:41 lmao he actually moved the background art thing up to perfectly not-cover the word "hookers." Obviously great presentation overall but I specifically appreciate the effort that went into this easter egg of a meme
good catch, hahah
haha yes he did, good catch :D
24:00 float reference abstraction - my god the sudden realisation that this is exactly what I was looking for. it's so elegant. THANK YOU
Can you please tell me the use for this, like what's the entire point of using floatvariable scriptable object & floatreference class?
@@herohiralal3255 i think this is what he said: You don't reference the "owner" at all to get the value because the SO holds the value. It's better and safer for designer because it gives them a GUI to create their own values with other SO features to help. Changes affect all scenes and any objects which rely on them(stops accidental prefab changes and is a form of serialization).
That's what I got.
"If (scene.menuname != menuscene)" is how I solved a problem in my game very recently. I had been pretty proud of myself for that. :(
This is pure gold, ten years working in Unity and I finally know this
this talk alone its worth going to unite
Does this guy have a book or a video series that he is selling somewhere ? Those architecture tips are golden, I would GLADLY pay for more.
Awesome talk, thank you! I personally get rid of using Editor to set references as much as possible as it's really difficult yo find those references later or make sure Unity did not broke them for unknown reason at some point. Doing everything via code saved my life but I will give a try to use scriptable object based event system.
I cant express how important the video is ... THis is one of the best video in UNITE, THE demonstrator is super knowledgeable and has shown powers of SOs, and is really good at demonstration. THank you very much
I started using these for pretty much everything. They solve pretty much every issue I typically have to deal with. I never wanted to use multiple scenes because it was a pain to serialize and deserialize data between scenes. Now it doesn’t matter. I can just drop in whatever I want without a care in the world.
This is very useful, its a shame there isn't people like him (that I'm aware of) making unity courses or books that go through how to apply this stuff in detail, I'd find it very useful to have a book/course that goes through how to apply this step by step rather than an overview.
On reading unity forums and subreddit, I always find myself keep getting back to this video, kinda interesting
I'm a big fan of scriptable objects already. But I definitely had not thought about some of these use cases. Thank you so much! Great talk! :D
Just awesome :) thanks a lot! Great pace, info and humour couldn't think of a nicer way to learn :)
That was an amazing talk! We definitely need more architecture guidelines for Unity.
This is so fantastic, it leaves me speechless. A breathtaking talk. Thank you!
I have just discovered this video and I have to say that it is amazing! For the first time, someone has explained me very well how ScriptableObjects can be used
While eye-opening, this presentation sent me down a two-day rabbit hole which consisted of me slamming headfirst into scripting mania and error headaches. Making float variables was the easy part. I can barely customize the editor to display them in the Inspector!
This is an amazing talk and it clearly outlines many patterns to make it easier for developers to crate games on solid games architecture
What an excellent, relatable and casual speaker this guy is. Great job, great subject! Thank you Ryan.
I agree, he was very easy to listen to, not condescending in the least, intelligent, articulate. I would like to see more game development topics presented by him.
Best talk ever! Although, I can’t help but think that he looks like Dennis Reynolds in the thumbnail lol
So very helpful and eye opening! Thank you, Ryan and Unity!
I'm so glad someone directed me to this talk. Had no idea how powerful Scriptable Objects could be, and will definitely be watching the Richard Fine and The Hack Spectrum talks next. Thanks Ryan!
This video is a classic! Thank you. I am watching it for the 3rd time and get something new out of it every time.
Been doing Unity at home for a couple years now and always knew I should be using Enums. I used them for the first time last night, spent a bunch of time setting up a bunch of stuff using them ... then added a new one near the top of the list ... OMG !! ... it didn't keep the values I set where I used them !! ... well THATS good to know !! ... and here I am :)
This is the best demonstration on how to use ScriptableObjects by far! Learned so much from it. Thanks fot that talk :-)
This is one of the best unity talks ever. Thank you very much.
This is fascinating. In a lot of ways, Unity and its particular setup seems to get in the way when you try to program from a typical perspective. I'm very new to it, so I'm not fully on board with its style of doing things yet.
You've created some very clever ways of doing things that embrace Unity's strengths and that's pretty awesome. This and Richard Fine's talk about ScriptableObject should be required viewing. Thank you very much! :-)
Thanks for this great talk, especially for the demos and use cases presented; still very useful in 2018. :) And exactly what I was looking for, because as a Unity noob (but someone who understands SOLID programming in .Net) I had a lot of dilemmas about how to implement loosely coupled design in Unity, and most sources don't answer this at all. I also appreciate the critical tone towards some Unity characteristics, e.g. the curly brace placement and relying on magic strings.
Wish we had an example of that inventory system, saving/loading and resetting those scriptable objects must be quite tricky. Not sure if I want to attempt that myself yet.
7 minutes in and love the talk already - great communication skills
this talk is amazing and has totally changed how i will approach setting up unity projects in the future!!
True, the FloatVariable scriptableobject thing blew my mind lol
Thank you for the re-upload! This is a great talk and the audio de-sync would've been a pain when I refer back to this while coding later.
This was an incredibly useful speech that hit many issues that I struggled with. Virtually every point I have worked at some point but was unsatisfied with my solutions. After watching this it's time to take another whack at it. Thanks!
You rock man! An inspiring talk and presentation, think I can't go back to repeating ill practices after learning from this session.
So gratifying when i see a system that resembles so closely a system i was already doing. Great talk!
For anyone interested - we've just open-sourced our implementation of this concept, called SmartData. It's comprehensive, powerful, extensible, includes custom editors and even a live node-graph visualizer/debugger. github.com/sigtrapgames/SmartData
VERY professional and well done talk. Thanks for the slides, blog and github code!
Good lecture
Keeping the code clean is super important
So to make sure that I've got this architecture concept straight, I'd like to use scriptableobjects for my multiplayer game.
For this reason, I'd like the shared configuration of this type - things like Max Health etc., to be FloatVariable, IntVariable, and so on as required (Or simply - BaseConfiguration to hold all of them together, increased locality).
In order to let our UI bind itself to the player's data, I'd like to use FloatReference, IntReference, etc (Or simply - PawnStateReference - holds everything you intend other systems to interact with, leaving other fields as true private fields for encapsulation)
Then, the player our UI recognizes will have PawnStateReference hold the PawnStateVariable SO, and other clients of the exact same type/prefab will use the "constant" (isn't runtime a better name?) instead.
Simple, client and other players share the exact same components with the player having an additional Input script that handles converting the inputs to actions via client pawn.
UI only knows what our client pawn is doing extensibly, and if I'd like to add UI elements per every other player, I can use a RuntimeSet of all pawns in the scene and access the PawnStateReference from there, as it'll be public. I don't even need to add a messy "IsClient" boolean or whatever because I could just compare the PawnState reference with the one my UI recognizes.
Is this considered an attempt to overengineer scriptableobjects into my game or does this seem like proper usage of the proposed architecture?
Thanks.
I love that this person used the Pillars of Nosgoth as the image.
i didn't know until now that scriptable objects are important for game architecture. I always wondered how Developers manage to do this in AAA Games. Really eye opening talk. Recommended Talk!!!!
I'm still loving it
Use it in every project :)
Thank you so much haha
I'm a very new programmer. I started learning C# & Unity Development in late July of 2020 (it is late September 2020 as of writing this.)
This talk has helped me out so much, that I cannot even describe the sheer thankfulness, and happiness I have from it.
I'm currently at that point where I fully understand C# on a fundamental level. I'm familiar with the dynamic of value types & reference types on an intuitive level, and I am also familiar with all data types and how to properly implement them in accordance to eachother.
The problem I do have though- what is the most efficient manner to use all of these tools together, in order to create the cleanest, most efficient, and modular manner?
I really really like the system that he presented here, and I am going to be using it from here on out. This is a fantastic way to create systems that are decoupled from eachother. Utilizing the data types are the shared resource between different systems is a take that I have never even considered doing. I always thought that you HAD to reference system to one another, in order to create any sort of communicative functionality between said systems.
This talk is amazing.
I really like the modularity and designer-centric focus of this approach. I use Zenject now and like it, but it's often not quite as easy to pivot with zero programmer involvement.
This is a brilliant approach- thank you for the talk and for the upload ^_^
Holy mother, this is what I've been looking for! Great talk!
So basically variables that may need to be shared between different "systems" could/should be individual SO's so it's easy to do so. That's pretty cool!
This is one of the most amazing talks I have seen.... Demonstrating stuff as you say about it... Yayyy to Scriptable objects.... Btw that we are not monsters line was XD
RuntimeSet {
List items
}
Why, god!?
(amazing presentation and content)
I tested it and combined it with pluggable approach. this architecture really challenges other architectures.
Authorable for non-coders and easy to maintain
this looks great and the talk is huge and very well done, It's just really hard for beginners to understand how to correctly implement them, or this is how I'm feeling
Amazing talk. This will help me hugely. I was soo annoyed having to hard wire everything. Not anymore!
I like the idea of using an asset as a source of publicly communicating some data. It reminds me of a Unix Socket in the unix/linux world, a sort of public channel for reading/writing data.
This is super nice talk. I've used this architecture in a couple of
games and found myself often re-writing the same boilerplate code (for
events), so I've found a really nice open-source library for it:
github(dot)com/chark/scriptable-events. I really recommend using it!
This talk, and the 2016 talk by Richard Fine are huge eye-openers, and greatly appreciated! I was wondering though: would it be a good idea to make the GameEvent and EventListener classes generic? That way, it can be parameterized for other generated UnityEvent types.
Just tried the event system. Works great!
This talk has helped me tremendously!
Thank you so much :)
Very nice presentation, thank you very much Ryan!
Really good and helpful talk!
43:47 when the camera man is sleeping :)
Great concepts here. Thanks for the talk
An amazing talk. Thank you so much!
Wow. What a fantastic presentation!
Great talk! One downside that wasn't mentioned is that the event binding is only visible in the inspector, which makes this approach a little more unfriendly when viewing the source code outside of the Unity IDE.
Good Job.
This is a re-upload of a previous version of this video that had audio sync issues. They also could've been there in person or maybe they're even a co-worker or friend that helped edit or critique the presentation. Why would you put someone on blast just for saying, "Good Job"?
If I'm making an rpg with X amount of characters, and each of them has an inventory. If I tried to use this pattern, would I just create one inventory for each character? Or is that a case where Scriptable Objects stops being a good idea
This kind of depends on a few more factors. ARe the number of characters set? Is this a case like Final Fantasy where all your possible party members are named and pre-defined? Or is it more an RPG where you create your own party members, and it's an arbitrary number? The more things are static and known in advance, the more useful it is to use SOs for this. The more dynamic, the less useful. But even then, you can still instantiate SOs at runtime and set their properties as normal. At the end of the day, they're just normal C# classes.
I have some strong doubts about this:
- How do you manage execution order? Let's say, when my player dies, I want some object to react first, then other objects.
- Your whole code is tied to the Unity engine, which means it's a lot harder, if not impossible, to switch to another engine that does not function the same way. In my opinion, you should only use Unity as a view, and have all of your code logic and data somewhere else.
- How do you manage large number of entities? As far as I know, Unity Objects can only be accessed on the main thread, and Unity Events generate quite a lot of garbage when you have big amount of entities.
- You're basically writing something quite similar to visual scripting, except you don't have the benefits of visual scripting, with the nice flow visualization and debug options.
- How do you quickly edit data, when it's all over the place in many different scriptables? You have to create a huge editor window to edit everything?
- When in a smaller team, if you don't have a real QA department, how do you do code reviews? I tend to think the other way around, anything that is pure code can be easily reviewed, while scenes, gameobjects and components are quite annoying to track.
However, I have to admit there's a lot of advantages as well, especially that system dependency thing which seems quite nice to work with.
I can't answer these in depth, but one thing I have found that works very well is implementing interfaces in your core codebase with scriptableobjects. This way, you aren't inherently tied to Unity for obvious reasons and can still get all the benefits of using scriptable objects. I do the same thing with monobehaviours as well.
It's certainly an interesting approach and it plays to the strength of using Unity as a tool for building the game in-engine versus in your IDE.
You pointed out some valid criticisms, I think it really depends on what the team needs designers to do and what the artist/designer/programmer ratio is.
Looks like great stuff but I failed to understand some details (I am a novice in Unity). I wish there were more examples and code, less common phrases. Could anyone explain how FloatReferences are supposed to be used when we have not a single player with specific HP, but a bunch of different instances of units with their own individual HPs (like in RTS)? Are these FloatReferences HP have to be created for each unit at runtime?
The same question is for Events: how it would be if an event could be raised by multiple sources (I don't want it to be received by all subscribers, but only those related to this specific source/instance)? Should we create a separate scriptable object for every event source?
Could anyone provide some links to more complicated examples?
I feel you man, I tried to replicate his examples in a little project having a player and multiple enemies. I tried using his UnitHealth and just putting it on the enemy but as you said it would reference the same health the player references. I'm now Instantiating the health SO at runtime but finding it difficult to get a reference for that from the health bar.
I would love for someone with experience to chime in with some wisdom.
I'm late but I would have the enemy have a MaxHealth FloatReference and a private float for currentHealth inside it that it tracks by itself. You only need to use this stuff for DI (Dependency Injection) anyway. Idk tho I've been a dirty Singleton user all my life so
If you are planning to have a constant number of human players in a given game session, ever say 4-8 then this approach would still work.
If you are referring to X number of dynamic enemies that could be more than the 10+ and that comes and go at any given time then I would avoid this approach as you’ll end up creating X number of HP instance per enemy, and that’s only HP we’re talking and not counting other properties.
However let’s say if each enemy had their own little HP UI above them, I suppose you could dynamically create an instance of the float variable with ScriptableObject.creatInstance() and wire that onto the enemy’s UI at runtime.
The guy in the video answers some of your concerns in a reply to a different comment here, posted by ReverendWolf, his name is Ryan.
Great talk, web lacks architecture advice for Unity developers. I wonder how they handle version source control issues using this approach. There are many changes after each game start, so it should be some type of runtime folder that must be in .gitignore file. But in that case game either will not start right after clone game repository or scriptable objects should be created by themself.
I use both, a singleton-scriptable-object. You can access it from every, without drag-n-drop the asset into the inspector everywhere.
You reintroduce the tight coupling problem that singletons bring though. And ScriptableObjects are accessible from everywhere anyway. They're assets, so you can reference them like any other asset from within another class as normal. Like referencing a texture or an icon.
Didn't play that part, but I saw it in game review videos. ;)
Kain!
Hey Ryan, awesome talk, really gave some good insights! Could you tell me how you achieve the selection the way it is shown at 20:55 ?
He explained at 22:33 how his FloatVariable scriptable object could be used as a parameter in the animator but he never actually showed how to do it. How do you actually bind the two? Do you actually have to do somewhere in an 'Update' method a `animator.SetFloat(myFloatVariable)` to keep it in sync? What's the trick here?
You may have meant at 22:10? He says "I'd like to see" as it's not possible. One good spot for that `Update` would be a StateMachineBehaviour that has a field for a FloatVariable. This would allow syncing the animator parameter to match the FloatVariable only while in animator nodes that rely on that parameter for a transition.
The biggest drawback really is the fact that you can't easily trace changes and diffs in source control. I understand the advantages of larger teams to delegate a lot of things to the editor when you have a lot of non-programmers. But otherwise that's really a pain point.
Working with enums isn't an issue with serialization, because you ALWAYS define the value for the enum to avoid these issues. But if some loss of performance isn't an issue, one can use the alternative approaches instead.
Yeah, I wasn't entirely sure why they couldn't safely remove certain enum values without breaking things. The compiler should be telling where enums that no longer exist are being used through errors.
One more observation - the notion that DI is exactly what the inspector is for and that there's no need for DI frameworks like Zenject is only partially true imo. It would make more sense if:
A. Every single class in your project that requires dependencies is either a MonoBehaviour or a ScriptableObject
B. You don't use interfaces
However in reality, you would want to use basic C# classes and interfaces and you can't inject their dependencies via inspector. Zenject also has many useful features like binding factories and memory pools. I would advocate for a combination of the inspector and a DI framework.
Interfaces can still work with the inspector! You serialize a reference to the UnityEngine.Object and use validation (OnValidate, ISerializationCallbackReciever, CustomEditor, PropertyDrawer) to reject the Object if it does not implement the interface. Then on Initialization (or ISerializationCallbackReciever.OnAfterDeserialize) you can cast to your interface and store it.
It sounds very hacky. I'm sure there are many ways to make it work. You could also purchase Odin Inspector from the Asset Store and serialize pretty much anything. But even if point B wasn't valid, there's still point A. I'm of the opinion that you shouldn't pollute scenes with objects that aren't 'actual things' in the scene, but are just there to run MonoBehaviours. The point is that the inspector cannot fully replace a DI framework.
This method requires that any consumers of a variable that are only concerned with changes in the value have to constantly poll the value and compare it to a previous state. This could have huge performance implications
Not if you make a variable object invoke a delegate on change! Some use cases benefit from checking every frame for data that changes a lot. Others can just get an event when it changes.
@@RyanHipple Yeah sure, you can alter what is shown in the video to do that.
Great Subject - Awesome Talk .
with Enum you can define:
enum Element{
None = 0,
Fire = 1,
Dynamite = 2
}
It's can't be re index in scene when you delete one define element
Great talk, Ryan!
You mention that the demo version of StringReference is a lot simpler than the one you use in production. I have been working on a very similar concept that is generic and free to use. I created a forum post for this, as well. As of yet, no one seems interested, so I would love some feedback from you (or anyone for that matter)!
For anyone else interested in this, head to the forums to share your thoughts and questions. Or, better yet, start to contribute on the BitBucket repository!
BitBucket Link: bitbucket.org/nobrainerinteractive/referenceablevalue
Unity Forum Post: forum.unity.com/threads/referenceablevalue.505389/
Cheers,
David
Thank god he doesn't use a Mac. Don't know why that always bothers me so much. Also, funny how they think people won't notice he is using an MSI laptop.
Anywho, great talk. Definitely one of the most informative and insightful
Very cool. Very enlightening. Thanks!
Outstanding. Downside, I not have a lot to recode and reconsider.
I'm a game artist looking at this guy's forehead and I think I just unlocked the ability to visualize better shading in my head now. Thankyou Ryan Hipple.
What if we had a class, like "ApplicationInitializer" which makes all the singleton classes according to what we want, (like DemoLevelManager or ReleaseLevelManager, both can be LevelManager.Instance) and the singleton works as a way to reach those that have common function. All LevelManagers have to implement or handle, for example, LoadLevel() or invoke LevelFinished, or have their own distinct way of having LevelGoalTracker. In this way the singleton is still modular because we are injecting the singletons in ApplicationInitializer, and no race condition because we are in control of initialization order in ApplicationInitializer.
Edit: Yea, Dependency Injection. But then how would we easily access the info we need? Every Enemy class has a reference to EnemyManager? What if an unrelated code wants to go through all enemies, it would want to reach EnemyManager.
Awesome talk, I really like the floatReference idea, but there is something I struggle to understand. How can I use float reference for runtime instance of object? Like the buildings health in your RTS exemple. We can put all the SO in runtime sets or dictionnary but it seem to be a "hacky" solution. And I can't figure how runtime sets and events can resolve totally the need of singleton or static reference, if I want the room where the player is standing or if I want to call a function of my saveSystem with parameters the singleton seem to be better.
I'm asking myself the same question for runtime instance. Have you had an answer since then?
Correct, SO was never intended to be a one stop solution for all. Understanding and combining SO usage where appropriate along with singleton just gives you an upper hand when making decisions in architecture.
However, If you don’t want the singleton approach and want a more robust gameManager for larger scale games, then you can look into using a Dependency Injection framework.
You make a MonoBehaviour called Scope. Inside the Scope are dictionaries where SOs are keys. Values of the dictionaries can either be local copies of the SOs, real values (like float, bool, int, etc) for a little better performance an less memory, or a cacheable index of a list that has the real values for the best performance. Inside your other code, you call a method like Scope.GetFloat(SOFloat). Then, what GetFloat does is try to use SOFloat in a dictionary to get the value to return and, if it doesn't find it, an entry is created, initialized, and returned.
PD
For the cacheable index of a list. You'd cache the index inside a FloatReference instance, you'd pass that FloatReference to GetFloat instead of an SOFloat, and you only get the index from the dictionary if the SO referenced by FloatReference has changed or if it's being used with a new Scope. That way, getting the scoped value, most of the time, just means getting a value at an index from a list which can be a lot faster.
Any thoughts on how to pass a variable(s) with the GameEvent? GameEvent like Event
Did you figure out a solution?
Wondering how you go about passing different kind of values alongside events. Events without any args seem not that useful.
also agree with most of the commenters, this vid sold me to scriptableobjects. I can honestly say, I've never used a singleton.
I checked out the github. There is a ScriptableObject class for float and one for string, but none for int. Why would that be?
you need to create one for a int, same for all your variables that u wanna use