Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

please point me to a good pattern for reusing objects and code between scenes?

Discussion in 'General Discussion' started by pinfamy, May 16, 2023.

  1. pinfamy

    pinfamy

    Joined:
    Apr 8, 2023
    Posts:
    10
    i'm really struggling to decide and implement the right pattern for reusable code in my game. i'm new to unity development and searching these forums, the documentation, chatgpt, etc... don't really give a clear picture of the "right" way to do what i want.

    my game probably started off like many first time game developers with a single scene. however, i've been writing code and subsystems that are completely reusable knowing that my game would benefit from multiple scenes for standard level design. i have many different reusable components/managers i've developed like:
    player input, save game, dialogue, interactions, a game manager, abstract enemy classes, scriptable objects for things like loot, etc...
    and a few components that will be specific to the scene like:
    the enemies that are specific to that scene, camera views, level specific logic, etc...

    i plan to have ~5 scenes that represent major worlds/levels in the game (as well as a start scene). they will be different enough that i believe they will warrant scenes because i already do things like manipulate the camera, switch from 2d to 3d perspective, and plan to play with things like physics/gravity in some levels. so assuming this is enough variance to push me into a better design over a single scene abstracting reusable code is a must. i'm not going down the path of copy pasting between 5 different scenes or cluttering logic specific to a scene into a very large and unmaintainable scripts. i'm now at the point where i've completed the introduction level to my game and i'm ready to load the next scene and here's where i hit the proverbial wall...

    i'm not seeing the clear winning model to reuse all the shared code, objects, and the things i have no clue about now and will run into later. should i have all my global/shared subsystems in a scene that cross reference the scene the player is playing? should i use the DontDestroyOnLoad for these subsystems? is it both, somewhere in between, or something completely different that i'm not yet aware of? i've spent a day or so toying around with both the global scene and DontDestroyOnLoad patterns and i got neither working immediately so instead of going down the rabbit hole on one of them and get it working i wanted to be sure (whatever i end up doing) it was the right long term choice.

    i went through the documentation and got zero love:
    https://docs.unity3d.com/2021.2/Documentation/Manual/CreatingScenes.html

    i assume this question has been asked multiple times, please tell me dupe and point me to the answer. i cannot find it in these forums, google, chatgpt, etc... the answer i get is: it depends. and in either case not a lot of great examples of projects on how to actually implement it correctly. thanks!
     
  2. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,900
    There are not much answers because you're asking a very abstract question. Your question is roughly the same as the question:

    What is the optimal method to go to the grocery store?
    The answer is 'depends':
    - how far the store is
    - do you have a car
    - do you have a bicycle
    - do you have some taxi service
    - do you have proper public transit in your area
    - how much and heavy stuff are you planning to buy
    etc.

    Just like you can't give an universally optimal answer, you can't give an universally good answer to your question. Because it depends on what you're actually doing. There are some outlines though:

    - fabricate prefabs, make sure they are more or less scene-independent (if they need to interact with the environment, they need to initialize their connections to the scenes they are loaded into)
    - go for composition, one "feature" one script, attach the script to prefabs which need the feature
    - make sure you write abstract enough code (if you have 10 separate skills which goes 0 to 100, you make an abstract Ability script and then fabricate your 10 named abilities out of this abstract one)

    I could go on and on until I fill out two full book worth of space. Reusable code is not a pattern, it is a feature of your code and architecture.

    My advise is to first make it work, then when you need the very same thing, make it general. It's easier to rethink the existing feature how to make it abstract so fits both of the cases than think ahead and trying to write code for the future. It rarely gives good results.
     
    Ryiah likes this.
  3. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,453
    Not really sure what you mean by this. Do you mean having a scene with the objects and then use additional scene loading to load the levels? In that case I'd not recommend that over using DontDestroyOnLoad because it gets messier.

    Since this is your first game, I'd assume you do not have massive, complex things that need to live across levels but just some score counter, inventory and similar.
    "player input, save game, dialogue, interactions, a game manager, abstract enemy classes, scriptable objects for things like loot, etc..."
    Everything besides dialogue and game manager can be entirely "stateless" aka they could even be created and destroyed any time.
    Generally try to minimize the "state" aka the data that needs to live not only across levels but even during levels. That will also make it simpler to save the game progress if the information that describes the current situation is not scattered everywhere.
    As a side effect you have less to worry about living across levels too.

    I recommend to just have a setup scene and activate DontDestroyOnLoad for the few corresponding objects.

    By the way, note that scene templates are a thing: https://docs.unity3d.com/Manual/scene-templates.html
    With 5 levels probably not crucial to use because you will end up editing your scenes often, but when your game is further in development and you decide for more levels, it can be handy.

    True dat. There's a good old software dev hint: KISS
    https://dev.to/kwereutosu/the-k-i-s-s-principle-in-programming-1jfg
     
  4. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
    You know that you can have more than one scene loaded at a time, right? Just put all your game managers in one scene that you keep loaded all the time and put your levels in different scenes that load and unload whenever you want.
     
    Ryiah likes this.
  5. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    a lot of times modularity and reuse will introduce unwarranted complexity, slow production down significantly, and in the end you won't have needed it anyway.

    if you have two widgets and its taking you hours to figure out how to make one knob that services both of them, just forget the whole thing and make two different knobs. It's not the end of the world and most likely everything is going to change anyway, so if you spent five days making the most modular knob it would have been wasted anyway.

    It's your first game, just focus on keeping it simple, getting it done fast, and look for lessons learned after it is finished. If its your first game and you are worried about "long term choices" you probably ought to scope the thing down a ton.
     
  6. pinfamy

    pinfamy

    Joined:
    Apr 8, 2023
    Posts:
    10
    @lurkinggruedropbox yup, i totally get that and why i was asking if i was to go multiple scenes are there any obvious pitfalls to avoid. an abstract question i understand, but right now i was debating between DontDestroyOnLoad and a second scene loaded in parallel.

    @DragonCoder suggested another one given my details that maybe i just keep these managers local and create a template. this may actually be the way i go because i've even integrated my dialogue and game manager with my save game manager to save state. the logic in both of those look up the last saved state and decide what to do next properly.

    @kdgalla yup, that was my "global/shared subsystems" pitch. they are all in a second scene which is loaded additively and does not go away. what may be a pitfall is that i get this warning immediately when implementing that:
    Cross scene references are not supported: '4x3Canvas' in scene GlobalSubsystem has an invalid reference to 'UICamera' in scene World1Level0. This reference will not be saved in the scene.
    chatgpt said the following in response to it:
    "By default, Unity prevents cross-scene references in the inspector and logs warnings whenever such cross-references do occur (e.g. when moving a GameObject with references to another scene)1. However, it is possible to explicitly allow cross-scene references by calling EditorSceneManager.preventCrossSceneReferences = false;"
    which is why i decided to ask some humans for some help. (really appreciate it by the way, and i hope someone else
    gets something out of this discussion too...)

    @BIGTIMEMASTER totally get it. i'm an admitted perfectionist... writing this to refresh my coding skills for interviewing and also because it's a lot more fun than studying algorithms and data structures.

    i guess what i'm really hearing is that i could likely get away with a single scene but i need to learn more about manipulating scenes. for instance my game is a 2d drop down, and i made a 3d particle effect transition between levels, which i assumed would be scenes. i guess what i can do is make the view somewhere else in the spaceworld, swap cameras, then swap to the new location of the level all in the same scene if i try to do this all in 1 scene.

    i really must say that i appreciate the thoughtful responses so quickly.
     
  7. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    If you can design your code so that all of it can be instantiated and destroyed as you change a scene, with only data (!) shared between scenes but no statics, no singletons, no DontDestroyOnLoad then you have a winning model.

    It may seem counterproductive at first having to serialize all game stats that should survive scene changes, be it writing it as json file or to PlayerPrefs or something else. But the real benefit is that this gives you a clear separation of the state that must be serialized vs any volatile state, and thinking hard about this separation always wins no matter if it‘s Unity, Unreal or a Database app. Especially considering that now you can start debugging sessions in world 3 without a great deal of setup code or having to play through worlds 1&2.

    I can wholeheartedly recommend the Robert C. Martin series like Clean Code and Clean Architecture. Just considering these cases and knowing where it‘s okay (for now) to bypass/skip some rules helps a lot writing better code in any environment.
     
    Ryiah likes this.
  8. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,900
    That's not me but never mind. :)
    Well, again, there is no 'best practice', you can achieve goals both ways. It depends what you use those for. In order to guess, we need to know more about the __concrete__ implementation you have. 2D-3D, platformer says nothing. Have no relations to how you can make your code and architecture flexible and reusable.
    I, for example, only work with additive scenes lately. At least for my own projects and when I advise my clients. But I purposefully build my own implementations usually and I mostly use 3rd-party stuff for one-shot things like making terrain in Gaia in a separate project so I don't leave Gaia in my production, only the terrain. Or editor extensions to help with certain things, like Odin Inspector to enhance the editor.
    I rarely use 3rd-party code, so it's easier to fit everything in the loading everything additively and keeping everything in Addressables and using the editor in no-domain-reload mode.

    But that's the thing, it is purpose-built.
     
  9. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
    I tend to put a singleton resource locator in my "main scene" that will have a reference to my one-off manager type of objects like gui and camera and stuff like that. Any objects that get loaded in can just access that.

    Also, these manager type scripts tend to be unique in all the project, so various transient objects can also find them using findObjectOfType most of the time.

    I wouldn't trust cross-scene references TBH, it sounds like they might be fragile and unsupported. Who knows.
     
  10. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,000
    Something I try and do is to avoid putting too much code into monobehaviours, especially if the code has high potential for reuse outside on a MB. Instead I'll try to keep code in classes and simply use a MB to reference or wrap those classes. This is because in the past I've found that MB come with their own baggage and its way to easy to tie code that could/should be generic to MB functions, making it difficult to reuse it elsewhere, extended it, swap it out etc.
     
    spiney199 likes this.
  11. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,317
    Attach a component which will locate dependencies on scene load.

    Perfectionism will get in your way when you run into something like fizzbuzz problem. This is similar. A situation where you use whatever works, but solutiosn are not perfect.
     
  12. IllTemperedTunas

    IllTemperedTunas

    Joined:
    Aug 31, 2012
    Posts:
    605
    Put all your game manager assets parented under a root object, call it "Managers" or something. Make this a prefab, make the children unique nested prefabs if you wish. Now if you want these to be unique in each scene, open each scene and create a prefab varient of this assets and call it "Managers scene 2".

    You have options from here, you can either put all the UI stuff, cameras etc in these managers prefabs and just have all the references inherent to that set of prefabs and move them about and adjust them per scene per prefab varient, or you can add a tag to the one object you want to referenc in the scene like "UI Panel 3" or whatever. On awake have a script find that object with that tag. So long as it's in the scene it will find it and you can assign it that way.

    Once this is set up, you can go to prefab and apply changes to the parent prefab to propagate these changes to all scenes, or you can apply to that prefab and propagate them only to that specific scene.

    If you're not too familiar with prefabs this can take a little getting used to... but honestly you should start getting good with prefabs, they are one of the greatest things about Unity.

    One small benefit to prefabs is it reduces the massive weight that is put on your level files, mistakes and data all start being inherent to these prefabs, and less on one MASSIVE level file, which is really nice. Cuts down on the notion of "how the heck did this happen". You will also notice less bugs as many prefab assets tend to play nicer than one level with unique references all over the place. If you're using version control, it wlll also keep your work nice and tidy as you work on singular prefab assets and not overarching levels.

    Once you become more prefab minded, your level design process with get a lot better, you'll think modularly and organize things much better. Just wish there were more options to adjust selection settings per prefab as clicking on any object will always select the parent prefab and sometimes this isn't what we want when childing various prefabs under others.
     
    Last edited: May 17, 2023
  13. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    I like to keep all my managers and sub systems completely outside of scenes, or let them bring themselves into existence. I noticed manager game objects were becoming a massive hinderance, and my processes sped up a lot more when I was able to drop the player spawner/prefab into an empty scene, hit play, and everything works.

    Having prefabs you're required to drop into scenes is just going to slow you down, lead to errors, and makes testing impossible.

    I approach this in a few ways depending on the situation:
    Static class - Good for simple managers, particularly when they don't need specific values prepared. Do need to take care that they reset themselves appropriately.
    Scriptable Objects - Great when you need to serialise values, or communicate values across active scenes. Can easily be made into singletons so they're good for global config assets as well.
    Lazy Initialisation - Useful for monobehaviours that only need to exist once. They can instantiate and DDOL themselves to hang around for the remainder of the session. Good for things like an Audio Manager.

    This is specific to manages, of course. And ideally they should be small entry points to a larger API.

    ALSO, keep your systems separate, ideally with assembly definitions. Set these up right at the beginning of your project, and keep the major aspects of your code base separate from one another, with some dependant on others.

    In terms of general re-usability, well that's what components are for. Ideally most components should be small in scope, do a specific thing, and do it well. If you have inspector addons like Odin, you can make use of
    [SerializeReference]
    and really ramp up the re-usability.
     
    tmonestudio, neginfinity and Ryiah like this.
  14. IllTemperedTunas

    IllTemperedTunas

    Joined:
    Aug 31, 2012
    Posts:
    605
    OP states this is an overarching system that ranges from top level managers to in scene objects of various types that require unique positions and setups per scene. Though the prefab/ prefab variant system can take a little getting used to, and sure, the interface is a little wonky between children and parent prefabs and even after you've gotten the hang of it mistakes will happen (but this is true of any system), it all depends on what you're comfortable with, it's a great mid level solution for someone just approaching game dev and a fantastic aspect of Unity for someone new to start adopting.

    Is it as water tight as a super tight system that generates itself at the start of a scene with fewer points of error and user input? No, but it's far more easier to adjust per scene and open ended without any additional code requirement whatsoever, allowing total freedom in every property between variants without any headache of implementation. Both solutions have their ups and downs.
     
  15. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    I (personally) think the smaller bits and pieces that live in a scene should generally be considered separate from the overarching system/subsystems. The 'system' is the stuff that does all the important nonsense to keep things running in the background. Then the actual 'gameplay' stuff, ergo, the game objects and their components, can all just hook into these systems, usually from their own assembly.

    I guess what I'm saying is I find bottom-up systems more flexible and reliable than top-down.

    I don't think there's anything particularly complicated with prefabs. They're the bread and butter of Unity. But keep them for when you need to reuse art assets or small gameplay components. Level assets, jump pads, power ups, etc.

    I (again personally) think it's wise to never require a particular prefab to exist in a scene for the level to function. You will see a very quick increase in your ability to introduce new overarching elements without breaking everything else, or requiring you to go through every bit of existing content to update it.

    Yes it's more code, but I think this is where being a solo dev has its advantage.
     
  16. pinfamy

    pinfamy

    Joined:
    Apr 8, 2023
    Posts:
    10
    it'll probably take me a good few days to absorb all the information here and even longer to put much of it to good use...

    i love all the ideas being brought into this discussion. i definitely understand all the coding concepts described, but the specifics in how to do them in unity are still new to me (you can literally see from my account creation i've been at this a little over a month) and unity brings a lot of new concepts that aren't just coding best practices.

    i ended up deciding to go down the multiple scene rabbit hole and started refactoring my code, scenes, templates, etc... i was a bit ambitious on my first attempt and pretty much mangled what i had done for the last month. no worries! a git reset --hard works magic... except when you rename scenes and delete old ones it seems. i went to bed frustrated with one stale and completely broken scene instead of my 5 shiny new ones, but slowly recovered back to a working state this morning.

    my strategy now is to baby step each subsystem to see if/how it needs to be transitioned to support multiple scenes by first creating the simplest start menu in existence and transitioning from start->level1 from there. already went through the pain of figuring out how to deal with unity's Event System (there can be only one) as well as my InputManager which i had used to wrap unity's Player Input as well as the Event System. i've decoupled those now after discovering they did not in fact need to be tied together so there's at least one victory so far. these are the types of things you just can't know till you figure them out...

    my prefabs, systems that save/load from json, scriptable objects, etc... i think will be pretty straightforward. for the rest and things that are tied to UI/game objects i'm going to try to avoid singletons and DontDestroyOnLoad as much as possible, and once i go from level1->level2 i'm certain it'll take some refactoring and decision making. i'll come back to this topic to chew on all these great tidbits over the next week. thanks again!