Tasharen Entertainment Forum
Support => NGUI 3 Support => Topic started by: Ferazel on September 24, 2013, 06:03:26 PM
-
I started to get my project upgraded to NGUI 3.0, but I'm really concerned with how the UIPanel is breaking up the draw calls per the editor breakdown. In the past I have didn't use the depth of a widget really at all. I had been using the Z position of the widget in order to maintain depth ordering and creating new UIPanels if I needed to sandwich. However, after upgrading I ran into some problems.
UIPanel1
|
---Sprite Depth -1
---Label Depth 0
UIPanel2
|
---Sprite Depth -1
---Sprite Depth 1
---Label Depth 10
Assuming the labels use dynamic fonts, I expect UIPanel1 to be 2 draw calls and I expect UIPanel2 to be 2 draw calls. However, what I seem to be seeing is that the UIPanels are splitting their materials and draw calls on a global level rather than a per panel basis.
If I assign a unique depth to each widget the panels will seem to sort this properly. However, my game UI has a couple hundred widgets and if I have to maintain a unique depth for each widget at a global level it will be a huge PITA. I expected that the UIPanel would sort their draw calls on a per panel analysis of the depth of the widgets.
Is this global depth check a bug or is it intentional?
-
Which panel the widgets are in doesn't matter as much in 3.0. Panels only mean that draw calls will be split up, but the order in which widgets appear is based solely on depth. In your case, you will want to make sure that the depth of widgets on your first panel is lower than on your second (assuming you want one to be in front of the other).
-
I always work thinking about my panels as dividing my UI. I think it would be more intuitive if optionally you could specify panel-depth that all it's widgets adhered to. That way you always know whatever depth u put widgets on in a panel they won't go above/behind some other panel. 8)
-
I'm disappointed that you feel this workflow is better. I hope I can convince you to change this. Or to have you help convince me why this change is an improvement.
From what I'm understanding in order to keep the UI optimized I would need to maintain a spreadsheet of depths for different areas of the UI so that I can make sure that I don't split materials on different depths causing unnecessary draw calls. There could be potentially thousands of widgets in a UI with all of the different dialogs/warnings/tutorials that could appear.
If we have potentially multiple layers of UI visible on the screen at any time. Each layer will need to have its own "depth range" that will need to assigned. Let's say that the dialog range is 200-500. Then on top of this, if there are multiple dialogs that share the dialog depth space, I would need to give each dialog a subset of that range. Otherwise in the Editor they are going to split because there are widgets that might conflict with each panel's atlasing. Then if we need to make a change of moving a chunk of the UI forward or backward in z-space this changing potentially every widget that has been created prior in order to make sure that they still fit in the appropriate z-depth? On top of that there is no multi-widget depth offset and must be done on a per widget basis?
I feel this is a disastrous change and very difficult to manage a UI a complicated as our last game's UI was (50+ dialogs/tutorials/etc.).
Before, I could slap the appropriate widgets into a parented panel. Check the UIPanel to see how many draw calls this panel had only factoring in its children. Adjust any depths internal to the panel and then not have to worry about other panels potentially interfering with this panel's draw calls. If I needed to adjust this panel's Z-Depth I could do it at the transform level and all of the internal widgets would maintain their internal depths.
If you feel that this new depth sorting is better, my recommendation would be as follows:
- Make each UIPanel manage the draw call for the widgets under it like the prior system. It will not check/compare depths of widgets on other panels.
- The Transform's Z position still has no impact so people don't get confused about render order there.
- Add a field to UIPanel that would affect the offset of the children widgets by some depth offset value (like 100 for dialogs).
I BEG you to explain why you feel this new 3.0 UIPanel draw ordering is superior. I understand that you were inundated with support emails from users who didn't understand the old system. However, I feel the new system requires a significant amount of work to maintain and is less flexible to changes.
-
You only have to worry about keeping depths reasonable among the stuff that's visible at the same time. If you have a window-based system like in Starlink, this is not an issue at all, for example -- since there only one window can be shown at a time. I basically didn't even have to alter any depths after upgrading to 3.0.
If you do have several windows that can appear in front of each other, such as I had in the Menu Example that came with NGUI, then it's an issue -- but all you have to do to fix it, is to select the root of one of the windows, and bring it to front via the NGUI menu. That's 4 mouse clicks -- one to select the object, one to open the NGUI menu, third to open the Selection sub-menu, and the last one to choose Bring to Front.
If you need to do this at run-time, you can do the same thing there. NGUITools.BringForward (not sure if it's in the D version, but it's there in the Pro version now at least). Even if it's not, I posted the code for it in another thread. It's basically NGUITools.AdjustDepth followed by NormalizeDepths, the latter of which is optional.
-
The new depth-only system works great and I'm glad you moved away from using z-position since it was prone to mistakes. I'm a big fan of the way UIPanels list draw calls to debug and optimize them. I was surprised to see some of my panels being broken up into 5+ draw calls. It turned out NGUI was splitting draw calls with adjacent panels (identical except in position) that weren't on screen.
I could reorder them, however, they are instances of the same prefab and there are panels below and above them. It's easy to bring a group of widgets to the front or back, but not moving them in between other panels without adjusting almost every other panel. In addition, I'm using NGUI as a 2D framework for my card game (a card is composed of ~16 widgets using 6 depths). I move cards between panels such as hand of cards panel that is anchored to the camera and the tabletop beneath it. Each player has a play space on the tabletop, which is a panel (the prefab mentioned earlier). Beneath the tabletop is the table background and above the hand are the menu panels. Moving a card between panels is not an easy task. Normalizing the depth is not an option since it doesn't leave depth space in between panels.
I like using the depth-only system so I wrote a Depth Tool (http://ericraue.com/f/external/unity/NGUI_Depth_Tool.unitypackage) that let's you define an offset for child widgets to start at and then it normalizes them. Simply add the UIDepthOffset script to any game object and enter an offset. Both relative and absolute positioning are supported so you can have nested UIDepthOffset scripts. It can also calculate how much depth is used by the child widgets, but it's not a cheap operation. I also wrote an editor window similar to the Panel Tool to visualize the UIDepthOffset scripts in the scene and added a NGUI menu option to recalculate all depths. I've been using my tool heavily for a day now and it's more intuitive than the old z-position system since it only uses depth.
(http://puu.sh/4AREi.png)
I would like to see a similar system added to NGUI to manage the depth of groups of widgets like Ferazel recommends. However, I think the offset shouldn't be tied to UIPanel as you may want to do this for nested objects that are part of the same panel.
-
I agree that having some information on WHAT is causing the depth conflict on a particular UIPanel would be helpful. I agree with zazery about having a depth management tool. Ideally, it would be a separate window that we could reference that displays the UIPanels and their associated depths and which panels have overlapping depths.
While we're brainstorming about depths. How about a "clean depths" button on the UIPanel that takes all of its children and removes any depth conflicts and may also have a depth offset. When the depths are normalized the lowest depth stays the same (or becomes the start depth value), depths that are the same will become previous used depth +1, empty depths are removed, then each widget's depths are set to be a unique depth on that panel, and we can easily resort the depths of that panel by doing this cleanup with a new start depth value.
Regardless, a global lookup of the depths that each UIPanel gameobject uses would just be helpful to diagnose where these conflicts in depth are occurring.
-
The easiest way to resolve depth conflicts is to bring one panel forward by selecting it then hitting ALT+SHIFT+= to bring it to front. This also normalizes the depths.
-
Hi ArenMook,
I'm finally back to work today after a being ill. So I'm slowly getting my UI system converted to the depth system. After a couple hours of converting work to this depth system, I'm still not convinced that this is an improvement.
While the Bring to Front helps at a macro setup level. I still want to stress that this still doesn't bring a comprehensive way of understanding the depths that are out there in the entire global space. Expecting the user to have this understanding is not as easy as it was prior. Previously, we could make assumptions that panels would not interfere with other panels in regards to draw call ordering. In 3.0 the depth space that have to be remembered and organized has increased significantly. For example, I was looking at my 2D UI panels trying to find the UIWidets with unique materials that were assigned depths that would be breaking my draw call batching only to realize that the problem was with panels that I was using in my 3D portion to display text and icons as well as dynamically instantiated effects and labels. :( It's infuriating to say the least and doesn't seem to have a solution.
I am trying to advocate for each UIPanel to have its own localized depths instead of trying to grasp all of the depths at a global level. While this singular depth it's nice in a simple UI like Starlink, most of the time designers are not thinking of us when they design their UIs. I have 2D UIWidgets which have at least 4 layers of overlapping windows (Tint & Highlighting, Base UI, Dialogs, FullScreen). Then these layering systems have conflicts in my 3D UIWidgets just makes it more complicated to manage. In my opinion it doesn't make sense at all that these completely different uses of NGUI would be interfering with each other at a global level.
Is there a workflow process that maybe I'm missing in order to help keep track of all of these panel depths? Is there a way I can make sure that instantiated panels don't interfere with panels that were created via the editor without having to call a line of code after every instantiation?
While it sounds like uGUI uses a more complicated algorithm for determining depth/draw call batching which sounds great. However, this depth issue is still a problem with NGUI as I mentioned above.
For other that might be reading this, how are you handling this change from <3.0? I've seen a couple people having problems with the transition too, but having other people here saying that "yes this is a problem, and it sucks" or "I've mitigated this problem by doing X,Y,Z" would help me out.
Thanks!
-
It takes some getting used to. I've been converting my project and so far, I've gotten around it by defining certain depths for things.
I have a base depth: screens = 0, popups = 50
then I split it up in
background elements 0-9+base
content level 10-19
fonts 20-29
overlay 30-39
and I have to make sure that some times they'll have to overlap because of weird design, but generally that's the rule of thumb.
We also have multiple cameras working so that we can have perspective 3d elements in the otherwise orthogonal 2d menu, so there's another headhache on top of it.
Furthermore, the colliders still work on a z position level, so I still have define constants for z position in the style of the depth positions above.
I love the new way the individual panels show their draw calls, but I wish there was a different window where I could see all panels in that sense. I also would like it to be easier to jump back and forth between the drawcall widget and back to the panel again, so you can quickly fix superfluous draw calls.
-
Thanks for the response Nicki.
So basically, you have resorted to taking a spreadsheet approach to organizing the depths. Something that makes it difficult to globally think about.
I think the biggest issue I've had in the conversion is the problem is that I had a workflow of placing panels and then populating those panels knowing full well what order the individual widgets would render. Now I have to do that at a widget level thus 10x the amount of work it is to organize these panels.
For example, my project has the following layers on multiple UICameras and having all of the panels interact is presenting itself as a problem.
- 3D Text/Sprites (these are sprites and labels that are rendered with my 3D scene). These are procedurally instantiated from prefabs. So all prefabs must be updated to use this depth area.
- 2D Elements (happens in a 2D space, but below the UI)
- UI Standard
- Dialog 1 - Dialog Level (informational dialogs, query dialogs, etc.)
- Flying Notification Layer (Things that fly to areas on top of the UI).
- Dialog 2 - Full Screen Pause menu
It's a mess, but it's a complicated UI system for a complicated game. Using the new depth system has been problematic.
-
You can use this UIPanelInspector.cs file to show all the draw calls instead of only those managed by the panel.
-
Awesome thanks ArenMook! I loaded it up and it looks greatly helpful! I will do some more tests with it tomorrow.
-
I hope I won't step on any toes with this, but I'd just to help NGUI improve.
After using the new system for a couple of days, I have to agree with Farazel. While the new system is good in the sense it solves a problem that confused users before, the added effort that is necessary to keep draw calls down is turning out to be a big time consumer. It's harder for me to maintain a clear overview what's going on in the project, and building new UI elements is taking longer because I need to consider all other possible UI elements that are on screen at the same time.
It's also a big problem for dynamic interface elements. Before, I saved out root objects as prefabs (for example, Trading Cards), and we could instantiate these onto any other UI element. Now, we need to take that extra step of using BringForward a specific amount of times to make sure such a system doesn't balloon the draw calls to unacceptable levels. And this amount is dependent on the bit of UI the dynamic UI element will share the screen with. It just becomes confusing to keep track of it.
Making mistakes also impacts us more than before, because performance problems make it harder to test gameplay and code than visual errors.
Ferazels suggested some improvements, and I'd like to echo one in particular:
- Make each UIPanel manage the draw call for the widgets under it like the prior system. It will not check/compare depths of widgets on other panels.
The first would solve all the issues that I currently have with the new system and make NGUI better than ever, I would think. :)
-
I feel if depth was per-UIpanel instead of global it might be more intuitive and easier to manage. Maybe a separate "panel-depth" or something for panels then
-
Awesome thanks everyone for their feedback. The goal here is to try to come up with some solutions to this problem and hopefully help ArenMook fix the problem. The new UIPanel inspector is a step in the right direction, but it still doesn't solve the problem of having this global depth space being difficult to manage.
With the reduced functionality of the UIPanel I think the best solution is to have the UIPanel's responsibility to be the depth parent. The problem comes when you start parenting UIPanels (such as a drag panel inside of a dialog). If we don't want to the local depths on a per UIPanel system like it was before some other options are per UICamera or per Unity layer so that at least you don't have a global range of widget depth to deal with.
-
I also think the new depth system is quite unmanageable. Our GUI is composed of 1000 of Widget layered in 5 to 6 layers. It is unthinkable to manage those individually by setting a depth that can only be used once. I get it, that having a dual system with z and depth was confusing, but I think that going with depth was a mistake. The good thing about the z was that it had a local / global component, you could set the localPosition.z of a widget/panel/gameObject and it would impact the z order of its children. This would make it possible to fix z order at a local scale while having nearly no effect on anything on the global scale.
Now, there is no way to organize the depth of widget by grouping them. GameObjects and panels don't have depth.
I know that it is frowned upon to talk about Flash in a Unity forum ( ;) ), but the Flash's Display List is pretty awesome. It does away with the numbering of layers and insures that no two visual items are on the same layer. It uses a tree structure (much like the hierarchy) to order layers.
If we could have some way to organize widgets, have some base depth that we can set on a panel I think it could work. Otherwise it is just too tedious. We need to break the big problem of organizing depth into smaller ones so that we can wrap our mind around it.
Anyway, I'm just saying, what do I know I have just been doing UI for the past 8 years.
-
When instantiating prefabs inside a panel (for example objects inside a draggable panel), don't bring them to front.
If you need to adjust their depth, don't do so blindly. Drag & drop your prefab into where it's supposed to be instantiated in the UI hierarchy, adjust the depth there, then hit Apply. This way when you instantiate this prefab, it will already have the correct depth.
There is a lot I could do if I wasn't tied by certain Unity conventions, and tree based approach would indeed make sense. However it's just not feasible with the way Unity's hierarchy works right now.
The upcoming Unity GUI system does work with a tree-based depth approach where children are always drawn after parents, and you can change the order of siblings, and it works well -- but there had to be some code written on the C++ side in order to make moving of objects possible. It also is currently causing other issues that we're working on, but I won't get into those...
TLDR version is this: there is no perfect solution in a plug-in. The only way a perfect solution can happen is if the system itself (read: Unity) was designed with all this in mind to begin with.
-
But tell you guys what... I'm going to add a "depth" property to the panel in the next update. It will default to 0 (which will behave as it does now). Currently widgets are sorted just by their depth, but now it will first sort by panel depth, then by widget depth -- meaning that if you give a panel a higher depth value, its widgets will always be in front because the sorting algorithm will order the widgets to be last.
So if you were doing a popup, you'd just give its panel a higher depth value.
-
that sounds awesome! 8)
-
First off, thanks for taking all of this onboard Aren. And yes, that change does sound great.
Just a question (I assume the answer is yes, but just double checking) you will continue to provide support on NGUI 2.7.0 right?
Regarding a specific answer you had for me:
When instantiating prefabs inside a panel (for example objects inside a draggable panel), don't bring them to front.
If you need to adjust their depth, don't do so blindly. Drag & drop your prefab into where it's supposed to be instantiated in the UI hierarchy, adjust the depth there, then hit Apply. This way when you instantiate this prefab, it will already have the correct depth.
This works if the prefab only needs to be instantiated onto that specific panel. If the prefab gets used in multiple locations - for example, an inventory of cards, but also a pack of cards, etc, then this becomes much more difficult to manage.
But it doesn't really matter, because I believe your change will fix my issues with this.
-
Thanks for the panel change, I think it should make it work.
Here is another suggestion: have a way of having widget sorted just by z. My guess is that you didn't want to go that way for 3D GUI, but if we could set the ordering somewhere to only use z because you know you are only using 2D that could be nice. So we could choose to use depth or z, but not both at the same time like before.
I am really happy to hear that Unity3D will use the tree approach in the future, if we could use the hierarchy to organize our layering that would be sick. It would actually be better than what Flash provided.
And by the way I love that you created the width and height properties. I was quite tired of creating a Vector3 every time I was just changing the with. Also it is way more readable.
-
Thanks for listening ArenMook. I understand that you try to find compromises all the time and it isn't easy to weigh everyone's needs.
Here is another suggestion: have a way of having widget sorted just by z. My guess is that you didn't want to go that way for 3D GUI, but if we could set the ordering somewhere to only use z because you know you are only using 2D that could be nice. So we could choose to use depth or z, but not both at the same time like before.
Using a Z-Depth only sounds pretty appealing to me. Not to mention that we can multi-edit transform z values, but it would also allow us to parent transforms. This would also allow us to intermix 3D elements with our UIs as mentioned in this post (http://www.tasharen.com/forum/index.php?topic=5999.0). Which is functionality we lost with the UIPanel no longer determining the Z-Positioning of the rendering in a 3D scene. I figure that this concept doesn't appeal to you though as depth has always been present in NGUI (and it does have purpose in regards to 3D UIs and perspective camera sorting/rendering).
-
I'm in the middle of putting together a depth overview tool, so you can more easily handle many draw calls and set depths for many widgets at once. I'll post it here or maybe give it to ArenMook if he wants to include it or whatevers. This might be useful for more complex UIs, or at least that's why I'm making it - because I need it myself. :D
-
But tell you guys what... I'm going to add a "depth" property to the panel in the next update. It will default to 0 (which will behave as it does now). Currently widgets are sorted just by their depth, but now it will first sort by panel depth, then by widget depth -- meaning that if you give a panel a higher depth value, its widgets will always be in front because the sorting algorithm will order the widgets to be last.
So if you were doing a popup, you'd just give its panel a higher depth value.
Awesome!!!
-
I will never go back to using Z. It's as simple as that. First of all, I don't want to use the Transform. Second, Z is a float. Integers are faster, and easier to work with. Lastly, Z is just one of 3 components that defines the object's position in the 3D world. 2D and 3D UIs need to be consistent, and using Z one way with one, and another way with the other breaks consistency.
The panel depth has already been added yesterday, and is available for Pro users to play with.
-
so. what should I do without z order?
http://www.youtube.com/watch?v=KFA_x18yX3o&feature=youtu.be (http://www.youtube.com/watch?v=KFA_x18yX3o&feature=youtu.be)
And lots of stuff like this depends on z order in our project.
-
Panels and widgets both have Z. Use it. :P
-
Can you explane it better?
Now labels with name and exp everytime appear in front of BG sprite OR behind it(if I set depth < GB.depth in inspector)
-
All widgets are first sorted by their panel depth, and if the panel depth matches -- by their own depth. Higher depth causes stuff to appear in front.
-
Hello! I have 2 sprites, as "sandwich" in one main gameobject root. And I rotate root gameobject , to show one side or another side. And there is a problem! The depth is same, but z position ignored, and in my case, one sprite always is on another sprite.
-
Z is always ignored by default unless you mess with UIPanel settings. NGUI sorts everything using the custom Depth value specified on widgets and panels.