Search Unity

Accelerate Success Series: Unity UI Makeover Follow Up Q&A

October 13, 2021 in Games | 8 min. read
Person creating a game
Person creating a game
Topics covered

Follow up Q&A to Unity’s UI Webinar

On September 9, Unity’s Accelerate Solutions team hosted their first Accelerate Success webinar: The Unity UI makeover. Hosted by the two team leads, Andrea and Sebastian, this webinar gave our attendees a live demo on how our consulting team would optimize a poorly designed UI. 

Focusing first on how to find out that something is wrong with a Unity UI, Andrea and Sebastian took turns walking through tips and best practices on animation, list and grid views, Canvas hierarchies, dynamic layouts, Overdraw, texture and assets, Mask versus RectMasks2D, draw call batching, and much more! 

If you missed out on this webinar, but want to view an on-demand recording of it, you can still check it out here

This webinar was one of the most popular ones we’ve ever done, with almost 1,000 attendees from different backgrounds and from studios of all sizes showing up. There were some great interactions, but we, unfortunately, didn’t get round to answering all the questions that were asked during the webinar.

In true Unity form, we want to ensure that our creators are getting as much value as possible out of the resources we put out, so we’ve gone back to our recording, identified some questions that did not get picked up during the webinar and are answering them here now.

Andrea, Sebastian, and some members of the Accelerate Solutions games team are returning to answer the ten of the best-unanswered questions from the Accelerate Success: The Unity UI makeover webinar. 

Q: Can I use tweening scripts on the UI toolkit?

A: The UnityEngine.UIElements.experimental package contains some tweening/animation utilities, both for style and for values. Driving style changes from external scripts (DOTween, etc.,) has drawbacks, mainly in the potential trigger of a reevaluation of the whole UI layout, on any minimal change, every frame – especially with changes that straddle numeric precision barriers or changes that trigger type conversions to/from float.

Q: Is it better to use Animators or scripts to trigger certain button functions?

A: It’s best not to let Animators anywhere near the UI. Not only do Animators not “scale down” well to effectively process the kind of simple animations that are generally used in the UI, they also dirty canvases and force layouts every frame even when there’s no animation playing. 

It’s much more efficient to perform UI animations with a custom script, a tween library, or a Legacy Animation component. For more information, see the animation section of Ian Dundore’s Unite 2018 talk, “Unity’s Evolving Best Practices”.

Q: With a single Canvas, will a sibling child still be set to dirty even if its transform is not moved at all?

A: It’s not clear from the question what’s meant by “sibling child” here, but presumably we’re talking about something like this:

A (Canvas) |_ B (Child UI element) |_ C (another child UI element)

This would make the question “if B moves, will C be set to dirty?”. The answer is that although C won’t be set to dirty as an individual element if any of the children of A are changed and flagged as dirty, A rebuilds its entire Canvas geometry. The cost of the Canvas rebuild will be the same regardless of whether C moved or not.

Q: Why did the empty rect fix the dirty bubble problem? Is it only because it doesn't have a layout component?

A: Essentially, yes. The layout updates bubble up the hierarchy as long as there is a chain of LayoutElements and LayoutGroups. In an existing UI, it’s sometimes the simplest way to break such a chain by adding an empty RectTransform. 

Another option is to disable LayoutElement and LayoutGroup components after the initial layout. For example, in case of a static layout that only initially needs to be generated to adjust the UI to the current screen size, this is a way to limit layout updates as well. 

More information about this can be found here.

Q: Why does adding an empty RectTransform in the hierarchy fix the “dirty bubble” problem? Is it only because the additional GameObject doesn't have a layout component?

A: Essentially, yes. When a layout element has changed, Unity will keep searching up the hierarchy from the element that changed, looking for GameObjects with LayoutGroup components. When Unity finds a GameObject with a LayoutGroup whose parent doesn’t have a LayoutGroup, it considers that GameObject to be the “root” LayoutGroup and will recalculate the layouts for that GameObject and all of its children. So, the layout updates bubble up the hierarchy as long as there is a chain of LayoutElements and LayoutGroups. 

Any GameObject without a LayoutGroup component acts as a “fire break” in the hierarchy. In an existing UI, sometimes the simplest way to break such a chain is by adding an empty RectTransform. 

Another option is to disable LayoutElement and LayoutGroup components after the initial layout. For example, in case of a static layout that only initially needs to be generated to adjust the UI to the current screen size, this is a way to limit layout updates as well. 

More information about this can be found here.

Q: When we import a sprite as UI, does turning off 'Generate Physics Shape' in Sprite's Import Settings improve performance or reduce build size?

You should turn off Generate Physics Shape if you don’t need it – and you only really need it for Sprites which will be driven by Physics2D during gameplay, so it’s not much use for UI.

Having said that, we created some test projects to see how much of an impact generating the physics shape actually has on loading times and runtime memory performance and couldn’t detect a difference even in extreme test cases, so it’s probably not something to worry about too much. Physics mesh geometry is very small (pretty much just a short list of vertex positions), so it’s unlikely to have a noticeable impact on build size or runtime memory.

Q: What is the downside of using SpriteMesh?

A: Using SpriteMeshes introduces some overhead and limitations:

  • Editor iteration and build times will be slightly increased since the SpriteMeshes need to be generated.
  • Runtime memory and binary build size will be slightly increased since the SpriteMeshes need to be stored.
  • Switching the texture directly on the SpriteMesh via Script at runtime will not update the SpriteMesh. Make sure to update the whole Sprite in this case.
  • More vertices and polygons need to be rendered

Q: What are the differences of using tight vs rectangle mesh type on importer settings? Is it caching on tight, that makes it better, or is "rectangle" better? Is rectangle better than not ticking "use sprite mesh"?

The Sprite Mesh reduces overdraw by rendering sprites as a mesh that fits the actual texture shape quite well, rather than as a rectangle with lots of transparent pixels. The trade-off is that Sprite Meshes require more vertices/polygons to draw. 

It’s quite rare for modern GPUs (including mobile GPUs) to be limited by raw polygon count, but they can suffer when they have to render lots of long thin triangles, and unfortunately, the Sprite Mesh feature can result in a lot of these.

Ultimately, the only way to find out whether overdraw or long thin triangles are the lesser of two evils in your project is to try them both and profile the GPU performance. Also remember that GPU optimizations will not improve your frame rate if your application is CPU-bound, and Unity UI generally presents a bigger challenge for the CPU than the GPU. So make sure you’re optimizing the areas which are actually hurting your performance and don’t sweat the small stuff unless your profiling tools tell you it’s a problem.

Q: Any tips/tricks for using multiple canvases? 

A: Generally speaking, you should put a canvas at the root of every main screen/panel of your UI so that you can show and hide them independently without triggering geometry rebuilds in other parts of the UI. Then, for each screen/panel, it’s usually a good idea to create a number of sub-canvasses to group elements according to how often they update. 

For example, a dialog box might have one Canvas for its background/frame elements that never change, another one that contains a scrolling text box and a button (which only change as a result of the player scrolling the text or pressing the button), and a third Canvas that contains any animated sprites/particle effects/progress bars that update every frame.

The flip-side of this is that Canvases don’t batch together, so if you end up having some extreme number of Canvases (for example, every single UI element on its own Canvas) you’d end up generating a high number of draw call batches that might hurt performance. Balancing Canvas usage is as much an art as it is a science, and as always, the profiler is your friend for checking the results of any changes you make.

Check out our Optimizing Unity UI guide for any extra tips and tricks on all things UI. 

Q: How do you deal with multiple device resolutions on mobile? Is Canvas scaler enough? With the growing amount of new resolutions and the number of cameras on the phone, it’s a bit confusing on how to layer out the UI. 

A: Canvas Scalers and the anchoring/stretching of Rect Transforms is generally enough butLayoutGroups can help in the cases where those things don’t solve the issue. , Also, you should consider writing a script to disable LayoutGroup components once they’ve done their work if your UI elements don’t behave dynamically (resizing, reordering, adding/removing elements) after their initial layout.

The important thing on mobile is to just remember that you’re authoring the UI for a range of resolutions and aspect ratios, and to design the UI to be flexible enough to accommodate those constraints. Test your UI on a range of devices, don’t just pick a single device and try to build a pixel-perfect UI for that one specific resolution. Remember that you can set up and test different resolutions in the Game View, or use the Device Simulator to quickly check your work.

When your game needs to support a very wide range of phones, you might find that you have to author sprite textures at high resolution (for example, so they look good on an iPhone 13 at 2532 x 1170) but then find that those big textures look bad or cause memory issues on much lower-spec phones (for example, iPhone 5 at 1136 x 640).

If this is the case, you should consider authoring the UI sprites at different resolutions and loading the appropriate ones at runtime, using AssetBundle variants and/or Variant Sprite Atlases. You don’t need to author sprites for every possible resolution: Two or three variants is probably sufficient to cover the full range of most currently supported devices.

Here are some forum threads with good discussions about supporting different resolutions:

Understanding Canvas Scaler Screen Match Mode and Reference Resolution

What Reference Resolution and Game View Settings Should I Use?

For additional information on how the Accelerate Solutions games team can help you optimize your game UI, or overcome any issues you may encounter during game development, check out our webpage. Don’t forget that the Accelerate Success: Unity UI makeover is available to watch on-demand here


October 13, 2021 in Games | 8 min. read
Topics covered