CLEAN Game Architecture with ScriptableObjects | Unity Tutorial

Ойындар

Show your Support & Get Exclusive Benefits on Patreon (Including Access to this tutorial Source Files + Code) - / sasquatchbgames
Join our Discord Community! - / discord
--
I've long heard tales of how singletons are 'bad' - but for some time I didn't really understand why...
In this video, I'll exaplin why singletons have a bad rep (despite how easy they are to use) and one potential solution to help you keep your game's architecture CLEAN.
This approach should help you make a game that is:
- More modular
- Easier to edit things
- Easier to debug things
I hope you enjoy!
--
Timestamps:
00:00 - Intro
00:20 - The problem with Singletons
01:37 - Reading our Input from a ScriptableObject asset
04:39 -Replacing the movement code on our player
06:07 - Reading our Input (using a C# generated class) from a ScriptableObject
06:38 - Setting up an Audio Manager with NO references needed.
09:20 - Creating our sound object
10:30 - ScriptableObject Variables
Unite Austin 2017 - Game Architecture with Scriptable Objects
• Unite Austin 2017 - Ga...
---
In need of more Unity Assets? Using our affiliate link is a great way to support us. We get a small cut that helps keep us up and running: assetstore.unity.com/?aid=110...
---
Looking for some awesome Gamedev merch? - sasquatchbgames.myspreadshop....
---
Who We Are-------------------------------------
If you're new to our channel, we're Brandon & Nikki from Sasquatch B Studios. We sold our house to start our game studio, and work full time on building our business and making our game, Veil of Maia.
Don't forget to Subscribe for NEW game dev videos every Monday & Thursday!
Wishlist Samurado!
store.steampowered.com/app/23...
Follow us on Twitter for regular updates!
/ sasquatchbgames
#unitytutorial #unity2d #unity3d

Пікірлер: 80

  • @AnEmortalKid
    @AnEmortalKid2 ай бұрын

    You technically still have dependencies, you just depend on an event stream now instead of a concrete class

  • @SekGuy
    @SekGuyАй бұрын

    Really glad I found your channel. This was the puzzle piece I needed in order to understand how I can improve a lot of things in my project setup.

  • @RigorMortisTortoise
    @RigorMortisTortoise2 ай бұрын

    I feel like I finally understand the appeal of scriptable objects now, your explanation is so well put together. Thank you!!

  • @hldfgjsjbd
    @hldfgjsjbd3 ай бұрын

    Earlier I was all over singletons, but now I am using them less and less and my approach is separate static class with events. One class triggers, all other listen, and it’s so much better, because I don’t have all that dependencies.

  • @morgansmolder7891

    @morgansmolder7891

    2 ай бұрын

    The dependencies still exist. Now they just occur opaquely through an event dispatch instead of directly through an object reference.

  • @hldfgjsjbd

    @hldfgjsjbd

    2 ай бұрын

    @@morgansmolder7891 they are centralised indirect dependencies that are not breaking the game, easy to manage, change and adapt, which is the main point. Singletons on other hand are hard dependencies with additional headache of initialisation and existing game objects.

  • @morgansmolder7891

    @morgansmolder7891

    2 ай бұрын

    @@hldfgjsjbd Whether or not that is better depends on what you are trying to implement. Indirect dependencies are difficult to follow and obfuscate what parts of your code rely on each other. It is also a pain in the ass to maintain an event based interface when the behavior of your systems is constantly in flux, which is pretty much par for the course for gameplay code. In general this singleton vs event system stuff is white noise people like to pull teeth over. You can architect a good system with either approach, and it's very easy to convert one into the other if your code is structured correctly. If your code is structured poorly switching all your singletons to event based dispatch will not solve that.

  • @hldfgjsjbd

    @hldfgjsjbd

    2 ай бұрын

    @@morgansmolder7891 Surely, I ain’t argue or defend one approach :)

  • @midniteoilsoftware

    @midniteoilsoftware

    2 ай бұрын

    I'm a big fan of the Mediator pattern and using a single EventBus. Anybody can trigger an event without caring who subscribes to them. Likewise, subscribers don't care who/what triggered the event(s). But I still use ScriptableObjects for configurable data (like audio event data with a collection of audioclips, volume/pitch ranges, etc.)

  • @outoftime9071
    @outoftime90712 ай бұрын

    In your video about seamless loading, how did you make the scene to load when it traverses backwards through the scene? I can make it work forwards but not backwards

  • @MarushiaDark316
    @MarushiaDark3163 ай бұрын

    Very cool, though something to be wary of - your scriptable objects won't maintain their values between sessions in a build. So for the health variable, if you wanted player health to persist, you'd have to serialize it out to something like JSON and load the value back into the scriptable object. This can be quite deceiving since it will appear to work correctly in the Editor.

  • @Sim2322

    @Sim2322

    2 ай бұрын

    That last example where he stores 'current_hp' in a scriptable object made me threw up in my mouth in disgust.

  • @YoutuberUser002

    @YoutuberUser002

    2 ай бұрын

    Wouldn't just storing the initial value of the variable and setting it back to the initial value when done be the same because thats what I'm doing for my weapon SO for its damage I hace a double damage power up the I just set it back to its original value after the timer has elapsed that's my way doing this but idk if it's right

  • @Sim2322

    @Sim2322

    2 ай бұрын

    @@KZreadrUser002 Very best way to use Scriptable Object is to consider them read-only outside the editor. If you have to change them on the fly, something is wrong in the architecture

  • @YoutuberUser002

    @YoutuberUser002

    2 ай бұрын

    @@Sim2322 oh lol I'd keep most of my SO like that where I just reference the variables and objects from it but just not in that damage case

  • @Sim2322

    @Sim2322

    2 ай бұрын

    @@KZreadrUser002 that's the intended use. A classic example is a Unit class that has some health. Your class itself would hold a scriptable object of some Unit, which contains a hp_max variable. The hp_now value would NOT be in your scriptable object, but in your Unit itself. So yeah,basically hp_max goes in the scriptable object, and hp_now would be in the class. You'd really see the benefits either in larger projects, or when you work in teams, but it's a good habit to take as an indie nonetheless.

  • @jerradbieno6810
    @jerradbieno6810Ай бұрын

    How does using this approach for inputs work for things like the current mouse position? Do you have to send/subscribe an event everytime the mouse position changes? I'm using the singleton inputmanager style approach so I simply update a public field on Update to store the mouse position, but I'd love to use this SO approach for inputs.

  • @bgoldpanda7265
    @bgoldpanda72652 ай бұрын

    I love this architecture ❤️ this is what I structure all the new code I am writing my first game in. Completely changed the way I thought about game development and it makes life a whole lot easier. The only problem I haven’t solved yet is saving the SO objects i instantiate and how to save changes to a file and load them.

  • @sealsharp
    @sealsharp2 ай бұрын

    Theres one thing people need to understand about ScriptableObjects is that they work differently in runtime builds than they do in the editor. They are ASSETS. And they work like ASSETS. If you change scenes and the assets in the last scene like textures models and ScriptableObjects are no longer needed, they get unloaded. In the editor, writing into ScriptableObjects changes them in the asset database. In runtime it does not. So perceived persistence which makes ScriptableObjects appear liked "supercharged singletons" in the editor does not 100% apply to runtime.

  • @dreamcatforgotten8435

    @dreamcatforgotten8435

    2 ай бұрын

    Not true. You can simply set the hideFlags of a SO to HideFlags.DontUnloadUnusedAsset in their Awake, and you'll never have to worry about them unloading and getting Deserialized again with their Editor-assigned default data throughout the lifetime of the Application.

  • @allenbagwell8034

    @allenbagwell8034

    Ай бұрын

    private void OnEnable() => hideFlags = HideFlags.DontUnloadUnusedAsset; You've gotta manage it directly and call "DestroyImmediate " when you don't need it anymore, but if the intent is for the SO to hang around during the entire length of the game you should probably be attaching it to DontDestroyOnLoad.

  • @risingforce9648
    @risingforce96482 ай бұрын

    this approach is to avoid only singleton ? or make an event scriptab object system? thanks

  • @ekm95
    @ekm952 ай бұрын

    Exactly what I want. Thank you so much.

  • @rechnight
    @rechnight2 ай бұрын

    I used Scriptable Architecture a lot and was so hyped in the beginning, was making everything into SOs, events, variables, even references to script instances, until my project was full of SOs and I was getting lost of what was being referenced where... Now I just use a service locator...

  • @GabrielBigardi

    @GabrielBigardi

    2 ай бұрын

    A service locator is better but it's still bad and considered an anti-pattern, i recommend using a minimal dependency injection library or making your own.

  • @rechnight

    @rechnight

    2 ай бұрын

    @@GabrielBigardi Agree with you, I keep the service locator usage to a minimum necessary, mostly for the global services: Events, Audio System, Pooling, Saving and so on, and also instances of services specific to an entity. I do use a kind of Dependency Injection, but I mostly inject from the Service Locator for services and from an Entity Manager for components.

  • @connorjagielski6760
    @connorjagielski67603 ай бұрын

    I see you linked the GDC talk, but it would have been nice if you had at least mentioned it in the video. I mean you even used the same heartbeat sound concept as the talk on scriptable objects.

  • @dmnoStretch

    @dmnoStretch

    Ай бұрын

    13:00

  • @ggwp8618
    @ggwp86182 ай бұрын

    Love you bro. Sharing some quality knowledge. Can you please cover jobs sytem? I have learnt alot with your tutorials

  • @tenko1058
    @tenko10582 ай бұрын

    damn...Unity guides have gotten so much better since I started to use the engine. Wish I had a guide like this for setting up inputs back then.

  • @ragerungames
    @ragerungames3 ай бұрын

    Great video! I always prefer using scriptable object.. they are very powerful in many cases!

  • @Diablokiller999
    @Diablokiller9992 ай бұрын

    Pretty good but for events that fire often (like move), I would use normal C# events instead since UnityEvents tend to take longer and more ressources, at least in my benchmarks. Yeah it's some premature optimization for sure, but I go with if I need to assign in via Inspector use UnityEvents, else C# events. But if you already have UnityEvents all over your project, maybe stick with it for readability?

  • @_jonathancollins
    @_jonathancollins3 ай бұрын

    I literally was working on input this morning and this will come in handy! Does this method of handling input allow for custom key bindings?

  • @AnEmortalKid

    @AnEmortalKid

    2 ай бұрын

    Yes like key rebinding ?

  • @GabrielBigardi

    @GabrielBigardi

    2 ай бұрын

    If you mean changing ScriptableObject's to persist key bindings, then no. They are simply static assets and should be used like that, it shouldn't change at all.

  • @Fyres11
    @Fyres113 ай бұрын

    Nice. Another way I'm using SO to make the code more modular is using it to pass events. Kinda the same as you I can drag in the player, drag in the input or delete them in play mode and no error. Question about the audio manager. How do you make a music track persist between scene with that way?

  • @owencoopersfx

    @owencoopersfx

    2 ай бұрын

    You would need to expand on the example quite a bit to really handle it nicely, but at the core of it you’d just run DontDestroyOnLoad and pass in the instantiated GameObject that has your music AudioSource.

  • @GabrielBigardi

    @GabrielBigardi

    2 ай бұрын

    Please don't use this to pass events, specially UnityEvents, on a real project it will make your life A LOT harder, debugging becomes a hell, naming and organizing your SO's too, not to say about keeping tight coupled references of them on each script that will use them. Do it the right way and use dependency injection and depend on abstractions instead of concrete classes.

  • @Fyres11

    @Fyres11

    2 ай бұрын

    @@GabrielBigardi I'm not sure what's the problem with debuging? A debug log or error will lead me all the way down to the source even if there's a SO event (Not unityevent) in the middle. Its true I need to add them on each script that need them. In any case could you explain in more details your way so even if the player and the inputManager are on 2 different scenes (Player is a addon scene) they will work fine? What I got for now is both the input manager and player with the SO events on them for the inputs. The player script got DI into each of its state to use the SO events.

  • @GabrielBigardi

    @GabrielBigardi

    2 ай бұрын

    @@Fyres11 If the player and the input manager are on two different scenes, using ScriptableObjects might not be the best solution, Unity scene management system provides other ways to communicate between scenes, such as using DontDestroyOnLoad. ScriptableObjects as events introduce an extra layer of indirection, and while it’s true that a debug log or error can lead you to the source, the indirectness introduced by ScriptableObjects can still complicate debugging. It can be harder to trace the flow of data and control, especially in larger projects with many scripts and ScriptableObjects. ScriptableObjects persist their state across game sessions in the editor, which can lead to unexpected behavior and differences from playing on Editor and playing the real game's build. While ScriptableObjects events can work fine for smaller projects, they might not scale well for larger ones. If you have many scripts that need to listen to the same event, you’ll need to add the ScriptableObject event to each of them. This can become cumbersome and error-prone as your project grows.

  • @RodriGGod
    @RodriGGod2 ай бұрын

    Please make a complex inputsystem generating c# class Show us tap, hold, the combinatoon of 2 button, aim with mouse and controller at the same time. Thank youuuu

  • @iosandro43
    @iosandro433 ай бұрын

    i'm a big fan. I like your tutorials. I'm developing an app using unity. I encounter scroll rect( list of images one below the other) runs laggy in some mobile devices. I hope make a tutorial covering this issue. I TRIED ALMOST ALL SOLUTIONS I FOUND ON THE NET. but, I believe your lecture will the one which will help me the most. looking forward to your reply

  • @EudaderurScheiss

    @EudaderurScheiss

    2 ай бұрын

    i ended up rewriting the scroll rect from unity sources and smartly enable / disable it, while also optimizing it. whole unity UI is garbage and designed for desktop. shit i even rewrote the touch handler, because touching the screen causes 15fps drops. they literally recaculate the whole hierarchy for touchable inputs and bubble those events up. no issue on desktop, but mobile thats just stupid. precache touchable areas and calculate the touches only on changes, not calculate every ui elements recttransform on touch. after all optimizations i regret not creating my own ui system based on sprites, with a camera on it. canvasscaler is also crap. horizontalgroups. verticalgroup all crap. even the textmeshpro has issues, since every textfield adds an listener, that listenes every frame for canvas scale changes. animations & animators invalidate the whole thing, and lead to recaclulation. particles are not working. oh and draw calls are by hierarchy orderning, so batching does not work. (ok not much of an issue in rly new phones anymore, still just bad) oh and dont forget the camera setup, for screen space overlay and screen space camera is also not working idealy. and its annoying when debugging, because you click on every invisible ui element in the editor. ui in unity is bad in every way. optimizing it means rewriting it and even then its mediocre. -- sorry for the rant i got ptsd from that system

  • @iosandro43

    @iosandro43

    2 ай бұрын

    Can you share the touch handler you wrote with me? Thank you for replying

  • @bgoldpanda7265
    @bgoldpanda72652 ай бұрын

    Also could you please make a tutorial on assembly definitions 🙏

  • @snarf8115
    @snarf81153 ай бұрын

    I’m still watching the video, currently at 6:49. However you said using generated C# class doesn’t work for your game/style(?). Could you elaborate/show an example of why that is? When I first started learning the “new” input system, using the C# generated class was/is much cleaner and easier to use.

  • @snarf8115

    @snarf8115

    3 ай бұрын

    Also I really love the idea of you creating tutorials like this. I love the sped up, not skipped or normal speed code that you write as you explain it or if you explain it beforehand. one suggestion is to add comments to functions or certain code (as you’re typing them) so if we want to refer back to the video and let’s say want to read instead of listening, we have comments to go through. Or it can help to compare to what you’re saying and what the comment says. nonetheless, fantastic video so far. I am going to finish it up.

  • @lizardjoshack1839
    @lizardjoshack18392 ай бұрын

    And what is the difference between Scriptable Object and Static Solid?

  • @owencoopersfx
    @owencoopersfx3 ай бұрын

    Nice examples. I really like the SO AudioManager approach. Learning how to effectively use Scriptable objects is one of the best level-ups for a Unity dev.

  • @EarthtideStudios
    @EarthtideStudios3 ай бұрын

    Can you do this using the old input system? As that's what we're using for our build :)

  • @AnEmortalKid

    @AnEmortalKid

    2 ай бұрын

    Yeah probably. Your Input Manager scriptable object defines what events it sends. It listens to the Input system and fires those events when it makes sense. I feel that should work

  • @markguyton2868
    @markguyton28682 ай бұрын

    As a Godot user, I wish there were more tutorials like this for that engine. Then again, I don't comprehend code very well so I'm not sure how much of a difference it would make for my current situation.

  • @dutchiewonderz6553
    @dutchiewonderz65533 ай бұрын

    Great video just wondering if you could make a small adjustment in the future and avoid adding the rolling Early Access Supporters on the screen. It's very distracting while trying to learn from your video. Perhaps a static runner (footer image) without the text changing. Again great video and I appreciate all the hard work you put in - looking forward to more down the road.

  • @nnx7631
    @nnx76313 ай бұрын

    Hey man, I've seen most of your videos and I love them. I see that scriptable objects are really good way of creating a whole game, however I still cant grasp the idea behind their power. However, I saw your tutorial for FSM on enemies + scriptable objects. Could you maybe in future create a tutorial for FPS character that uses FSM with the new unity input system + scriptable objects? I think it will be super beneficial to many people because there is no solid example out there how to actually do it correctly. Thank you!

  • @dabmaster6874

    @dabmaster6874

    3 ай бұрын

    Scriptable objects is usual a way to contain data/behaviours. Its advantage is that the modification you do to the data within the play time session or in the editor/play mode are permanent. SO statemachine is one way to share data and behaviours between many scripts. In the case of a fps controller, the use of SO is overkill if used for behaviours. But a scriptable object for the data could help you test many configurations of the data you use within your fps controller. You can try using different data presets for movement speed, rotation speed , jump height or maybe different weapon data presets that use fire rate, muzzle flash and bullet types to make a shotgun, minigun, smg... For the input system you could also include SO to make different input keys that you can plug and play, I used and it was a very useful way to make modular pieces of code/ data driven behaviours

  • @fukodafufu6662
    @fukodafufu66623 ай бұрын

    Really cool video, and this type of architecture is indeed really cool to playtest and separate things. But I have to disagree and give a warning on the "easier to debug" statement, which I believe is false. Since a lot of stuff will be working with event and aren't directly linked to each other, the bigger your game is, the harder it will be to keep it understandable and easy to debug. And even more if you start working with other developers that'll have a hard time to understand why and what's going on.

  • @GabrielBigardi

    @GabrielBigardi

    2 ай бұрын

    I tried using this pattern on a project few years ago so i 100% agree with you, it makes things a lot harder to debug and on a real project it's not maintainable at all, i even made a comment talking about using SO's this way is actually worse than using singletons but i think he removed or something.

  • @fukodafufu6662

    @fukodafufu6662

    2 ай бұрын

    @@GabrielBigardi Glad to see someone that finally get it! I think most of the people loving this architecture are either making really small games, or are switching game before even finishing them. But I still think it's pretty cool to base a part of your architecture on it for the fast prototyping, like any pattern : Those are not omnipotent, they are good if you use them well for a specific context. People tends to forget that.

  • @foxes-oy6ip
    @foxes-oy6ip2 ай бұрын

    Just add a simple null check before referring to singletons can solve the dependancy problem already

  • @dreamcatforgotten8435

    @dreamcatforgotten8435

    2 ай бұрын

    I don't think you understand what solving dependency issues means if you think null checking is the solution.

  • @foxes-oy6ip

    @foxes-oy6ip

    2 ай бұрын

    @@dreamcatforgotten8435 I thought about it again. What he did was make the script depend on an SO file instead of the singleton. Why not just check if the singleton is null and save all this trouble? Also, if the script can in fact run independently, the best thing to do is to explicitly write out how it should handle the situation when the singleton does not exist, so as to avoid logic errors.

  • @harddev9181
    @harddev91812 ай бұрын

    TRY Game dev breakdowns there r good

  • @CyberAngel67
    @CyberAngel672 ай бұрын

    SO's are not the best use for this, as they can lose scope fairly easy.

  • @Coco-gg5vp
    @Coco-gg5vp3 ай бұрын

    First

  • @newsystem3667
    @newsystem36673 ай бұрын

    Now lets talk about performance

  • @OIndieGabo

    @OIndieGabo

    3 ай бұрын

    Nice. Can you start? What is concerning in this case?

  • @Sim2322
    @Sim23222 ай бұрын

    Ok guys. DO NOT DO THIS. Especially the very dumb last example where he stores a 'current health' variable in a scriptabe object. I repeat: NEVER, UNDER ANY CIRCUMSTANCES, DO THIS. Your project will break, it's that simple.

  • @dreamcatforgotten8435

    @dreamcatforgotten8435

    2 ай бұрын

    I think it depends on the project and how it is used. If the number of Players is static, and there is no chance of scaling the project up to multiplayer, it's reasonable to have data that would typically be considered 'local' to be on a SO. However, for Enemies/Multiplayer Scenarios/etc - things that should be dynamic - it makes much more sense to have current health be localized to a component of that entity.

  • @Cooo_oooper
    @Cooo_oooper2 ай бұрын

    This tutorial offers nothing new compared to the 2017 Unite talk this was taken from

  • @goldone01

    @goldone01

    2 ай бұрын

    First of all, I'm sorry, but why do you have to he so negative? Secondly, if you've already watched the unite talk, you are not the target audience. And I am surprised how it isn't obvious that a 13 minute, easy to follow youtube video has a different purpose than a more than one hour long unite talk.

  • @Cooo_oooper

    @Cooo_oooper

    2 ай бұрын

    @@goldone01 It's not negative, it's critique. It's literally the same content and code as in the Unite talk which already explains it in an easy way

  • @de0o0
    @de0o026 күн бұрын

    I don't like that architecture, you are using singletons, you do have dependencies and your data is not separated from logic

Келесі