YousicianGit / UnityMenuSystem

Apache License 2.0
154 stars 46 forks source link

Improved Prefab and Canvas Handling #2

Closed IsaiahKelly closed 7 years ago

IsaiahKelly commented 7 years ago

Menu prefabs are now loaded automatically from a resources sub-folder named "Menus". No need to add each prefab to a field on the menu manger itself anymore. Just make sure they're all saved to this sub-folder. From what I understand, each object field reference cost memory and this will add up if you have a lot of references. So loading these prefabs dynamically, without any predefined references in the menu manger, should save memory. It's also much more convenient because menu prefabs are now found and loaded automatically!

I also removed UI canvases from all the menu prefabs and replaced them with a single UI canvas on the menu manger itself. Newly opened menus now stay on top by setting their own child transform order, rather than canvas sort order. This should save memory and improve performance too, since we now only have one UI canvas for all the menus.

Nezz commented 7 years ago

Thanks for contributing @IsaiahKelly!

Menu prefabs are now loaded automatically from a resources sub-folder named "Menus". No need to add each prefab to a field on the menu manger itself anymore. Just make sure they're all saved to this sub-folder. From what I understand, each object field reference cost memory and this will add up if you have a lot of references. So loading these prefabs dynamically, without any predefined references in the menu manger, should save memory. It's also much more convenient because menu prefabs are now found and loaded automatically!

This approach indeed offers an improvement in memory usage and startup time. However, I advise against using the Resources folder to achieve on-demand loading of menus. Let me just put the official Unity recommendation here: resources https://unity3d.com/learn/tutorials/temas/best-practices/resources-folder

The modern Unity solution for the problem is to use Asset Bundles. You can read about it here: https://unity3d.com/learn/tutorials/topics/best-practices/assetbundle-fundamentals

One thing to keep in mind with optimizing memory usage is that you will generally end up sacrificing peformance. By loading menu prefabs on-demand, the menu cannot be opened instantly and the user will experience a delay when they tap a button. If most of your menus rely on the same set of textures and fonts, there will be very little memory benefit from on-demand loading.

If you have menus that use different sets of resources then Asset Bundles are the way to go. They can be either shipped with the app or can be downloaded on-demand. This allows you to ship a smaller app and even makes it possible to add more levels to your game without having to update the app.

Here is a memory comparison: https://blogs.unity3d.com/2017/04/12/asset-bundles-vs-resources-a-memory-showdown/ The Asset Bundle system has been optimized well lately, which makes it the optimal choice in most use cases.

Nezz commented 7 years ago

I also removed UI canvases from all the menu prefabs and replaced them with a single UI canvas on the menu manger itself. Newly opened menus now stay on top by setting their own child transform order, rather than canvas sort order. This should save memory and improve performance too, since we now only have one UI canvas for all the menus.

Bad news: You gain next to nothing by having a single canvas.

First off, when it comes to memory usage, having a canvas in every menu prefab does not make a visible impact. A canvas does not use more memory than a couple of kilobytes.

When it comes to performance, using a single canvas will give you benefits only in a few rare cases.

You have a single menu open and visible For example all menus that have DisableMenusUnderneath set to true. Whether you use a canvas in the menu manager or the actual menu, there will be one active canvas, so no benefit.

You have multiple menus open and visible For example your GameMenu and your PauseMenu on top (DisableMenusUnderneath is set to false). Rebuilds: If they share the same canvas, Unity will have to rebuild both menus whenever you move something in either of the menus. If they have their own canvases, the GameMenu will not be rebuilt if something changes in the PauseMenu and vice versa. Rebuilds are expensive and you will end up with worse performance. Drawing: The only benefit of using a shared canvas is that Unity will be able to batch draw calls from both menus. However, Unity can only batch draw calls if they use the same texture (textures on the same sprite atlas) and material, As soon as you start mixing textures with fonts, you will end up breaking the batches because fonts have their own texture atlas. Generally you will end up reducing the draw call count by 1, for example from 16 to 15.

From the Unity UI best practices:

In all but the most trivial cases, it is generally a good idea to split up a Canvas, either by moving elements to a Sub-canvas or to a sibling Canvas. Performant UI design requires a balance between minimizing the cost of rebuilds and minimizing wasted draw calls.

You can read more about the topic here, under "Splitting Canvases": https://unity3d.com/learn/tutorials/topics/best-practices/fill-rate-canvases-and-input

IsaiahKelly commented 7 years ago

Thanks for all the feedback @Nezz!

Yeah, I only used the resources folder instead of asset bundles here because I thought it would probably be easier for this example. The main goal was really just remove the need to have prefabs fields on the menu manager itself. I knew using asset bundles would be the recommended way to load them though.

One thing to keep in mind with optimizing memory usage is that you will generally end up sacrificing peformance. By loading menu prefabs on-demand, the menu cannot be opened instantly and the user will experience a delay when they tap a button. If most of your menus rely on the same set of textures and fonts, there will be very little memory benefit from on-demand loading.

I completely agree with this. I only optimized the on-demand loading here because that's how your example works. But I'm really confused now because this seems to be the complete opposite of what you said during your Unite Europe 2017 talk:

The good way is to instantiate menus on-the-fly when you need them. Instantiation is pretty cheap and it doesn't effect memory too much. So that's a nice thing and when you for example you go to the options menu, you instantiate the options menu. If you return from the options menu to the main menu. Then you can destroy the options menu cause you don't need it anymore.

So which version of yourself do you agree with?! 😛

As for UI canvases, I did not know about the rebuild issue. So thanks for pointing all that out. However, in my defense I believe the idea of using a single canvas was something I picked up from the official Unity UI example asset.

Nezz commented 7 years ago

In the quote from my talk I was referring to on-demand instantiation of preloaded prefabs. Here we are talking about changing the loading of the prefabs to be on-demand too.

Prefabs come with a bit of memory usage, but other than that they will have no impact on the performance of your game. They are just sitting in the memory, without being updated or rendered. However, once you instantiate a prefab in your scene, Unity will have to start "caring" about that GameObject (call Update functions for example as long as it is enabled). This will have an impact on the performance of your game. Also, when a prefab is instantiated Unity will create a copy, so you get twice the memory usage.

IsaiahKelly commented 7 years ago

I see. My PR did actually include an option to preload all the menu prefabs from the resources folder on Awake. Meaning this on-demand loading can be eliminated completely and work just like the original example.

However, with the number of menus your app Yousician seems to use I'm guessing you don't have some gigantic list of prefabs linking all menu prefabs to the manager and are using asset bundles in some way to manage the loading, correct?

Nezz commented 7 years ago

However, with the number of menus your app Yousician seems to use I'm guessing you don't have some gigantic list of prefabs linking all menu prefabs to the manger and are using asset bundles in some way to manage the loading, correct?

Yes :)