Search code examples
c#user-interfaceunity-game-engineunity3d-guiunity3d-editor

Managing complex prefab hierarchies in Unity3D


I asked this question in a few places, and still haven't figured it out completely yet, so maybe some smart people here will have an idea how to approach that.

What's the best way of maintaining a deep nested hierarchy of prefabs in a project? Say, we have a few GUI screens, made of smaller, generic components. For the sake of simplicity, let's call the former "views", and the latter - "components". Views deal with semantics (e.g. inventory view, store view); components are configurable, but have no business logic attached to them whatsoever (for instance, a button cares only about its OnTap callback/event handler).

Both views and components can be nested.

GUI views need to be reused across multiple scenes.

The main issue with this approach in Unity is the fact that any nested hierarchy, once turned into a prefab, loses references to its children prefabs, like in this (completely made up, yet still valid) example:

- storeView - UIViewHeader - UIHeaderLabel<Text> - UIList - UIListItem - UIThumbnail - UITitleLabel<Text> - UISubTitleLabel<Text> - UIPrimaryButton<Button> - ...

I'd like to keep all of these, small UI* components in separate prefabs, but also keep the storeView prefab so I can easily add it to different scenes. Unfortunately, as soon, as we create the storeView prefab, all of the UI* prefab references are lost. Given that, we could try a different approach, in which instead of having a storeView prefab with content, we keep it empty and pick one of these few options:

  1. attach a behaviour to storeView and load child prefabs during run-time
    • cons: makes designer workflow more complex, puts the complexity in the script, which might be more error prone from the dev perspective too
    • pros: makes it easier to reuse the storeView between scenes, component prefabs can be styled, modified globally
  2. keep the storeView as an empty prefab, and reassemble it in every scene that requires it
    • cons: components need to be wired up manually, it's still easy to save the entire storeView accidentally and lose the prefab references
    • pros: guarantees that component prefab references are maintained, allows for slight differences between views (imho that's an issue actually, since these should belong to the configuration layer)
  3. save the entire storeView as a prefab
    • cons: scales terribly, makes iterating on new features or small UX changes more time consuming (additional QA, acceptance tests, etc...)
    • pros: it's a quick and dirty solution that works well with small projects
  4. use Prefab Evolution or similar
    • cons: I assume the package will be rendered obsolete by Nested Prefabs, which are on the roadmap? Requires depending on 3rd party code and might not be flexible enough (any opinions here, guys?)
    • pros: From many (mixed) reviews I've seen, some have been quite positive. The more complex a project gets, the less positive these reviews are, however.
  5. write a bunch custom Editor scripts:
    • cons: time consuming, also - seems like something that should be provided by the platform, even if it deals mainly with games
    • pros: complete control over the behaviour, can be improved by developers with designers' feedback. One might argue that being disciplined about having implementation, tests and designer-friendly tools as feature requirements sounds like a good design practice (resulting in less technical debt, easier maintenance)

Here's my personal, v. idealistic, unrealistic solution to that*:

Use a componentised architecture, where child prefab references are stored in complex prefabs by default. Internally think of them as UIView and subviews on mobile (Cocoa), or component classes in React or better, functional components - React/Cycle/Elm/anything that goes well with FRP (yes, I know these approaches differ in so many ways, but the key is composability, achieved either by functional composition, decorators, et cetera, et cetera...).

  • cons: I assume that my difficulties here come from my lack of experience with Unity and there's a more obvious, perhaps idiomatic solution (which I'm kindly asking for:) )
  • pros: makes testing and iterating on new features way easier, makes prefabs way more powerful, whilst not losing any of their benefits (please correct me if I'm wrong, I'm still getting familiar with Unity)

  • please don't think I'm expecting all of that from Unity, but that's one of the possible directions even if 1% of that is true.

Just to make it clear, it's not a rant regarding Unity, as a developer working previously with mobile (native) and web, I find it impressing how many of my problems it solves, and how ridiculously simple some of these solutions are.


Solution

  • I'd probably maintain the composite views in separate Scenes. Simply consider these scenes as they were prefabs, containing only a single prototype object of the given class.

    Using SceneManager.LoadScene with LoadSceneMode.Additive you could even create some static factory method, like:

    public class UIStoreView
    {
        public static UIStoreView Instance()
        {
            SceneManager.LoadScene("UIStoreView", LoadSceneMode.Additive);
            return GameObject.Find("UIStoreView");
        }
    }
    

    With some naming convetion you can achieve something as simple as storeView = UIStoreView.Instance();.

    Not a universal / scalable stuff, but at least you have something lightweight / maintainable until they roll out nested prefabs (timeline uncertain as they say).