CLEAN Game Architecture with ScriptableObjects | Unity Tutorial

Поделиться
HTML-код
  • Опубликовано: 12 июн 2024
  • 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
  • ИгрыИгры

Комментарии • 79

  • @AnEmortalKid
    @AnEmortalKid 2 месяца назад +18

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

  • @hldfgjsjbd
    @hldfgjsjbd 2 месяца назад +7

    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 месяца назад +1

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

    • @hldfgjsjbd
      @hldfgjsjbd 2 месяца назад +3

      @@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 месяца назад +1

      @@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 месяца назад +2

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

    • @midniteoilsoftware
      @midniteoilsoftware 2 месяца назад +1

      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.)

  • @MarushiaDark316
    @MarushiaDark316 2 месяца назад +17

    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 месяца назад +6

      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 месяца назад +3

      @@RUclipsrUser002 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 месяца назад +2

      @@RUclipsrUser002 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.

  • @SekGuy
    @SekGuy 10 дней назад

    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
    @RigorMortisTortoise 2 месяца назад +1

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

  • @sealsharp
    @sealsharp 2 месяца назад +10

    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 12 дней назад

      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.

  • @connorjagielski6760
    @connorjagielski6760 2 месяца назад +5

    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.

  • @bgoldpanda7265
    @bgoldpanda7265 Месяц назад

    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.

  • @tenko1058
    @tenko1058 Месяц назад

    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.

  • @rechnight
    @rechnight 2 месяца назад +2

    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 месяца назад +1

      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 месяца назад +1

      @@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.

  • @outoftime9071
    @outoftime9071 2 месяца назад +1

    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

  • @ggwp8618
    @ggwp8618 2 месяца назад +1

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

  • @ekm95
    @ekm95 2 месяца назад

    Exactly what I want. Thank you so much.

  • @jerradbieno6810
    @jerradbieno6810 16 дней назад

    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.

  • @Diablokiller999
    @Diablokiller999 2 месяца назад +2

    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?

  • @bgoldpanda7265
    @bgoldpanda7265 Месяц назад +1

    Also could you please make a tutorial on assembly definitions 🙏

  • @risingforce9648
    @risingforce9648 Месяц назад

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

  • @ragerungames
    @ragerungames 2 месяца назад

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

  • @RodriGGod
    @RodriGGod Месяц назад

    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
    @iosandro43 2 месяца назад +1

    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 месяца назад +1

      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 Месяц назад

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

  • @_jonathancollins
    @_jonathancollins 2 месяца назад

    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 Месяц назад

      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
    @Fyres11 2 месяца назад

    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 Месяц назад +1

      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 Месяц назад

      @@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 Месяц назад

      @@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.

  • @dutchiewonderz6553
    @dutchiewonderz6553 2 месяца назад +2

    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.

  • @owencoopersfx
    @owencoopersfx 2 месяца назад +1

    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
    @EarthtideStudios 2 месяца назад

    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

  • @snarf8115
    @snarf8115 2 месяца назад +3

    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 2 месяца назад

      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.

  • @markguyton2868
    @markguyton2868 2 месяца назад

    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.

  • @lizardjoshack1839
    @lizardjoshack1839 2 месяца назад

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

  • @nnx7631
    @nnx7631 2 месяца назад +1

    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 2 месяца назад +1

      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
    @fukodafufu6662 2 месяца назад +1

    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 Месяц назад +1

      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 Месяц назад +1

      @@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-oy6ip 2 месяца назад

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

    • @dreamcatforgotten8435
      @dreamcatforgotten8435 2 месяца назад +1

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

    • @foxes-oy6ip
      @foxes-oy6ip Месяц назад

      @@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.

  • @CyberAngel67
    @CyberAngel67 2 месяца назад +1

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

  • @harddev9181
    @harddev9181 2 месяца назад

    TRY Game dev breakdowns there r good

  • @newsystem3667
    @newsystem3667 2 месяца назад +1

    Now lets talk about performance

    • @OIndieGabo
      @OIndieGabo 2 месяца назад +2

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

  • @Coco-gg5vp
    @Coco-gg5vp 2 месяца назад

    First

  • @Cooo_oooper
    @Cooo_oooper 2 месяца назад +7

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

    • @goldone01
      @goldone01 Месяц назад +7

      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 Месяц назад +1

      @@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

  • @Sim2322
    @Sim2322 2 месяца назад +3

    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.