Disclaimer: Opinions.
Recently there’s been an update to the way GUIs on Roblox work. It’s actually rather subtle, but potentially devastating if allowed to continue existing. It all has to do with the how GUI objects are ordered; which ones overlap which.
This concept is called “Z-order”, the name derived from two-dimensional objects having a third, secret Z dimension, perpendicular to their X and Y dimensions. Imagine it as a sandwich being smashed into a pancake. It’s completely flat, but the sandwich fillings are still stacked in a certain order, and which fillings you see when you remove the top slice of bread is determined by this order.
Roblox Z-order is determined by a variety of things. Though it is done primarily by the object-hierarchy, or the parent-child relationship between objects. It boils down this: child objects are drawn after their parent, and a sibling object is drawn after the sibling before it. I refer to this sibling relationship as “child-order”. The children of a parent object have a consistent, determined order to them, which can be seen by using the GetChildren method of an object. Any usefulness of child-order is claimed to be unsupported by Roblox, so you’re not supposed to be relying on it. But it’s real, and it’s there.
Because child-order isn’t truly supported, control over how GUI objects are ordered is done by their “ZIndex” property. In a nutshell, the current implementation of ZIndex divides GUIs into “layers”, with the higher layers being drawn after the lower ones. These layers work independently of the object-hierarchy, so it doesn’t matter what the parent of a child object is. It only matters when the ZIndex of two objects are the same, in which case the hierarchy takes over again. Unfortunately, this is a very limited system, as you are only provided with 10 layers. You might often find yourself relying on the hierarchy anyway.
Now, what did the recent update change? In short, it modified the way Z-order is determined. At a glance, the type of the object now has an influence on the order. That is, one type of object will always be drawn over another. Here’s what I’ve gathered:
There appear to be 3 groups of classes, each group having priority over the next:
- TextBox, TextLabel
- TextButton, ImageButton, Frame
- ImageLabel
Objects with the same priority are still handled by child-order. However, now you’ve got behavior like TextLabels always being drawn over Frames, and Frames always being drawn over ImageLabels. Hopefully, you can already see a problem with this. This “type-preference” behavior can be worked around by using ZIndex, which is already a problem in itself. But I’ll get into that later.
The reason for this update is that it’s, apparently, a massive increase in performance. I’m not informed in the internal workings of Roblox, so I have no idea why this would be. The reason for Roblox looking into optimizing GUIs is most certainly for the recent port of Roblox to the iPad. I love that hardware limitation encourages software optimization.
But here’s why I don’t like this optimization: It trades in functionality for performance. Ideally, when you’re only optimizing, you want to increase performance while maintaining functionality. But there can be a balance to it. If the reduction in functionality is minor enough to make little difference, or causes no disruption, then it’s a good deal. In the case of this update, it’s a bad trade.
In terms of Z-ordering, Roblox GUIs are already very limited, so imposing more limitations is not good. The problem with the current Z-order system is that it allows for, and encourages, terribly structured GUIs. I don’t mean the layout, or how the GUI looks when rendered. I’m referring to the structure; the hierarchy tree of the GUI objects, their parent-child relationships. Here’s an example of a structure under under the current system:
You want to create a shadow effect under a box. You first insert the shadow object as a child of the box object. You set the size of the shadow to be 1,1 scaled, so that it maintains the same size as the box. You set the position as an offset to set the thickness of the shadow. Finally, you set the ZIndex to one less than the box, so it appears under the box.
This is a bad structure for two main reasons. One is because it’s possible to get interference from other objects on the same ZIndex as the shadow. That is, some object will indeed appear below the box, but it may possibly appear above the shadow, which is not an intended effect. The other reason is the limited space of ZIndex. What if the box has a ZIndex of 1? There’s nowhere to put the shadow. Shift it up to a ZIndex of 2? Now you have to worry about which objects appear above and below, which is challenging to keep track of. You might eventually find that you’ve shifted it up too far, and hit the upper limit of 10. The structure becomes a tangled mess, since objects are allowed to go where ever they want.
Here’s a better structure, once again under the current system:
You create an invisible container object, and set it to the size of the box. You insert the shadow into the container, with a size of 1,1 scaled, then insert the box into the container, also setting it’s size to 1,1 scaled. You set the offset position of the shadow to give it thickness. With that, to change the size of the box, you just have to change the size of the container, and both the box and the shadow will follow suit.
Note how there was no changing of ZIndexes here. That means all objects are on the same level, so there’s no possible interference from other objects. You don’t even have to worry about ZIndex at all, because the structure occurs on only one ZIndex.
This recent “type-preference” update essentially destroys all hope of using the structure in the second example. In order to get objects to appear where you want, you’re forced to rely on ZIndex, and all the problems with the first example come rushing in. A better solution would be to fix the ZIndex problem entirely. Who knows? Maybe fixing ZIndex will increase performance!
This is where I start to lose track. Once again, I have no idea of the internal workings of Roblox and their GUIs. Maybe they’ve already tried a better system, but it ended up being slow, so they went with the current one. Who knows? I don’t. But I want to convey my thoughts anyway. So, here’s my idea-guy-like proposition:
Instead of having discrete layers, have ZIndex determine the Z-order of an object, but only among sibling objects. For example, if you have 3 Frames, the ZIndex of one Frame determines the order among those 3 Frames only, because they’re sibling objects. If possible, allow for a ZIndex of any number. What if two objects have the same ZIndex? This is where you can apply whatever crazy backwards optimizations you want. It’s fine in this case, because ZIndex is now reliable enough to be used.
In this proposed “sibling-ZIndex” system, the structure in the first example isn’t even possible. A user is no longer allowed to put a GUI on whatever layer he wants, because there aren’t any layers. You can call it a limitation, but that’s only in relation to the current system. This system prevents the problems apparent in the layered system. It’s no longer possible for objects to interfere with each other. There’s no longer a limited number of layers.
This system encourages a logical grouping of related objects. In the second example, you have the box and the shadow. These two objects are different, separate things. But obviously, they’re still very related to each other. This is indicated in the structure by the invisible container, which holds them both. It makes more sense than the first example, where the shadow is a child of the box. This structure doesn’t complement the actual layout. It suggests that the shadow is some sub-object within the box, like a button or a bit of text. But the shadow is behind it, separate from it, giving an illusion of depth.
The current “layered-ZIndex” system allows, and encourages, bad GUI structures like the one in the first example, by allowing a user to place any GUI object in any arbitrary layer. Not to mention the limited number of layers a user is given to work with. This usually goes unnoticed for simple GUIs. But for complex GUIs, this can cause grief very quickly. Roblox only provides the basic building blocks, so if you want complex widgets (scrollbars, lists, tabs, etc), you have to implement them yourself. While making them on your own can be a fun experience, it quickly becomes painful to do under the current system.
As Roblox changes and evolves, problems tend to accumulate. Some get fixed, and some get pushed down the backlog. I like to think that this update is temporary, while better solutions are explored. I guess we’ll have to wait and see.
vittroll-blog liked this
fire-bomber liked this
anaminus posted this