Tasharen Entertainment Forum

Support => NGUI 3 Support => Topic started by: helmesjo on May 04, 2014, 12:38:16 PM

Title: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 04, 2014, 12:38:16 PM
As the title describes, there are some issues with the positioning of the processed text inside the UILabel-rect (rect is moving correctly) when dragging the scrollView (I'm using UILabel with the "Unity" option).

While dragging, I can see the font "jump" (scrolling vertically) inside the rect (again, the rect itself is positioned correctly), as if it is rounding to exact pixels. However, the result makes it look odd.

Just put a label inside a ScrollView and drag (vertically) and you'll see it. If not, I'll dig for the exact font we're using.

Cheers!
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 05, 2014, 10:11:48 PM
I'm not seeing anything. I created a new scene, added a scroll view, added a sprite under it with a collider and UIDragScrollView, added a label on top and changed the scroll view to be Horizontal. Everything works as expected when I hit Play and drag the sprite.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 06, 2014, 03:20:51 AM
I'm not seeing anything. I created a new scene, added a scroll view, added a sprite under it with a collider and UIDragScrollView, added a label on top and changed the scroll view to be Horizontal. Everything works as expected when I hit Play and drag the sprite.

Hehe, I hope you mean vertically since that is what I wrote two times.. :D Anyways, I tried it horizontally also, and there is still a small "glitch" (even tried another font, Arial, and it's still the same).

Just do it like this:

- ScrollView
-- UISprite
-- UILabel (Dynamic, Unity-option)

But you are correct, it didn't happen with new scene... I'm having a hard time finding the exact setup causing the issue, but after some minor testing it appears to be an issue related to nested scrollviews at least. In my case I have the following setup:

- ScrollView (Horizontal)
-- ScrollView (Vertical)
--- UISprite
--- UILabel (Unity, dynamic)

If you zoom in close in the scene, you should see how the rendered text is "stepping" in the scroll-direction out of sync compared to the sprite. I have UIRoot set to "FixedSizeOnMobiles" & "Adjust By DPI = true".

I use the following settings for the UILabel:


Edit. It only happens while dragging the inner-most scrollview (even if dragged via inspector through the transform-component), so it's not related to the UIDragScrollView-script.

Edit2. Actually, it seems to be happening to sprites as well... If you have two nested scrollviews as siblings, one may stutter and one may not, making it look like they are moving appart from each other back and forth.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 06, 2014, 09:21:33 PM
Yes, I meant vertical. The scroll views begin as horizontal, and I did say I changed it. :P

Your dimension is not right... 269? That's not dividable by two, so if the label is centered you're going to run into certain issues.

And now onto nested scroll views... what do you mean by one may stutter and one may not? How are you moving both scroll views? How many panels do you have in your hierarchy and what version of NGUI are you using?
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 07, 2014, 01:00:56 AM
Yes, I meant vertical. The scroll views begin as horizontal, and I did say I changed it. :P

Your dimension is not right... 269? That's not dividable by two, so if the label is centered you're going to run into certain issues.

And now onto nested scroll views... what do you mean by one may stutter and one may not? How are you moving both scroll views? How many panels do you have in your hierarchy and what version of NGUI are you using?

Hmm... Can't find the exact label again but maybe I mistyped? Other labels have the correct measures and still "stutter". ;) Yeah, I'm sure it's hard to understand and I'm trying to avoid to have to record a clip, partly because the game is not released yet and also because I really don't have the time right now... :(
I'll give it a few minutes to try to setup a new scene and figure it out, until then just please keep it in mind when playing around (zoom in closely) and you just might see it! :)

What I meant with "one may stutter and one may not" was that if I dragged ScrollViewA (look below) all widgets (including sprites) did stutter, but no widgets inside the nested ScrollViewB stuttered (which was not the case in the other example I found... I think...). It's just plain out... Odd. :)

- ScrollViewA
-- WidgetA1
-- ScrollViewB
--- WidgetB1

Cheers!

Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 07, 2014, 01:38:36 AM
Okey, I took my troubled scene and copied the setup over to a new, fresh, scene and cleaned it up, only containing NGUI-components and setup a lot of different test-cases so if it's not another secret setting I have, you should see the "stutter"-labels stutter! :)

I exported the scene only (to not include scripts, +1 for me) and I hope it works!

GL!

Edit. I run NGUI 3.5.6
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 08, 2014, 12:39:28 AM
I had a look. By "stutter" I assume you mean the fact that it seems to move by 0.5 pixels instead of 1, which with the crispness factor turned on means that it gets auto-corrected by NGUI to whole numbers so that it's drawn all nice and crisp for you.

The 0.5 happens because you have IOS Drag Emulation turned on, and you have a giant container widget that doesn't fit into the panel. So it restricts movement to 50%, creating "tension", thus moving by half a pixel for each pixel you actually move. The panel ignores its own half-pixel offset for drawing purposes, ensuring that all vertices are snapped correctly. However if you introduce child panels in there, their half-pixel offsets are not considered, so you get mis-matched behaviour.

Get rid of those extra panels.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 08, 2014, 01:55:16 AM
I had a look. By "stutter" I assume you mean the fact that it seems to move by 0.5 pixels instead of 1, which with the crispness factor turned on means that it gets auto-corrected by NGUI to whole numbers so that it's drawn all nice and crisp for you.

The 0.5 happens because you have IOS Drag Emulation turned on, and you have a giant container widget that doesn't fit into the panel. So it restricts movement to 50%, creating "tension", thus moving by half a pixel for each pixel you actually move. The panel ignores its own half-pixel offset for drawing purposes, ensuring that all vertices are snapped correctly. However if you introduce child panels in there, their half-pixel offsets are not considered, so you get mis-matched behaviour.

Get rid of those extra panels.

I think I see what you mean, but as I mentioned this is not restricted to labels with crispness, but also sprites within the same "stuttery" panel...
Getting rid of those extra panels is not an options, because in our game we have horizontal and vertical scrollviews nested to create a rich and user-friendly UI (nested scrollviews really isn't anything weird at all, but instead very common)... Since nested panels/scrollviews are now supported, one would hope you took a look into it and see if there is any (temporary at least for now) solution..! Any tips on what I can adjust code-wise to minimise this "stutter"? Turning off iOS Drag Emulation alone doesn't seem to affect it... And crispness, well, it's kind of crusual (otherwise the labels look awful).

Edit. Also, turning crispness off on labels does nothing either.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 08, 2014, 02:06:27 AM
Turning off iOS drag emulation fixes it just fine here. I had to ALT+SHIFT+P on your UIRoot however. You had some objects there in the UI hierarchy positioned at non-integer offsets.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 08, 2014, 03:25:49 AM
Turning off iOS drag emulation fixes it just fine here. I had to ALT+SHIFT+P on your UIRoot however. You had some objects there in the UI hierarchy positioned at non-integer offsets.

ALT+SHIFT+P (Make pixel perfect) also didn't do it for me (still see the stuttering when the panel slows down, of course the same issue as with iOS Drag Emulation)...
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 08, 2014, 03:28:28 AM
Well, I'm on 3.5.9 atm and you said you're on 3.5.6. I've also cut down your hierarchy at the end, removing all those containers. And more importantly -- removing the UIWidget that also had a UIPanel attached to the same object. Or vice versa. NGUi 3.5.9 actually spits out errors when you try to do that.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 08, 2014, 07:19:06 AM
Well, I'm on 3.5.9 atm and you said you're on 3.5.6. I've also cut down your hierarchy at the end, removing all those containers. And more importantly -- removing the UIWidget that also had a UIPanel attached to the same object. Or vice versa. NGUi 3.5.9 actually spits out errors when you try to do that.

I think you underestimate the value of nested scrollviews, it's very common and required in many situations in order to create a good looking (and compact) UI.

You mean error for having a panel on a widget? Even if the widget is "invisible"? Before you do that, you should really consider redoing the broken UIRect-inheritance because for example with my constraint-system it is much easier to position things using invisible widgets, and then slap a panel on that (not saying I do it currently, but it's not an impossible scenario). Panels live their own life with offset etc. And a little on that topic about the redoing UIRect, few small requests that are very much needed:
[/list]
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 09, 2014, 05:13:44 AM
Panels draw things. Both panels and widgets derive from UIRect -- meaning they can both have a rectangle that adjusts the transform position. If you have two UIRects on the same object, whether it's a pair of widgets, or a panel and a widget, then which one determines who sets the transform position properly? And how do you determine which one's gizmos show up and become modifiable in the scene view? And which one of them should be adjusted when you do?

Think about what you're doing here, and why you can't simply make one be a child of the other, or why you need the panel in the first place.

Note I said panel. I'm not talking about nesting scroll views. You have more extra panels there for no good reason. If panel doesn't have clipping, it uses the screen as its rect.

Adding extra panels is only useful to break up draw calls intentionally, such as when you are trying to put an overlay on top of the scroll view. Any other time you are simply creating extra draw calls and reducing your performance.

Not sure what there is "broken" about UIRect inheritance, or what you're referring to.

Any of these hierarchies are perfectly valid:

Panel
- Widget
-- Panel

Panel
- Panel
-- Widget

Panel
- Widget
-- Widget

What's not valid, is this:

Panel, Widget
- Widget
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 09, 2014, 07:32:37 AM
Panels draw things. Both panels and widgets derive from UIRect -- meaning they can both have a rectangle that adjusts the transform position. If you have two UIRects on the same object, whether it's a pair of widgets, or a panel and a widget, then which one determines who sets the transform position properly? And how do you determine which one's gizmos show up and become modifiable in the scene view? And which one of them should be adjusted when you do?

Think about what you're doing here, and why you can't simply make one be a child of the other, or why you need the panel in the first place.

Note I said panel. I'm not talking about nesting scroll views. You have more extra panels there for no good reason. If panel doesn't have clipping, it uses the screen as its rect.

Adding extra panels is only useful to break up draw calls intentionally, such as when you are trying to put an overlay on top of the scroll view. Any other time you are simply creating extra draw calls and reducing your performance.

Not sure what there is "broken" about UIRect inheritance, or what you're referring to.
...

I totally understand why you can't normally do it (and the scene I attached was just a exaggerated example), I just said that with the current inconsistencies between UIPanel & UIWidget there are bigger problems to solve (they both inherit UIRect but none share any behaviour pretty much, UIRect just delegates the calls to the derived class and handles nothing to do with sizing).

What I mean about UIRect-inheritance being broken is just this... From the coders perspective, there should be NO difference in positioning, sizing, changing edges etc. between two subclasses deriving from UIRect, since as the name implies it is the UIRect that should be the actual rectangle, not each subclass having it's own interpretation of it! As of now, positioning, sizing etc. is totally different for both of them, and adding more abstract methods that just delegate everything to UIPanel & UIWidget just mask the problem. How you are not seeing this I'm not sure, since you admitted it in another post that UIRect as of now "is more of a hack to make the anchor-system work"... I do also understand that UIPanel & UIWidget differ in what they do (scrolling in panel etc), but that is a problem which can be solved much more elegantly than how it is currently and shouldn't limit the structure of UIRect. I can't nag enough about you taking a look at a real constraint-system for setting up the UI because if you do then you will really understand what I'm trying to clarify here.

Not my intention to sound harsh here, but when I get the feeling that you don't see the big issue with the current design of UIRect/UIPanel/UIWidget I really have to point it out explicitly. With the current design, I consistently have to maintain custom modifications and "hacks" to make it work in a manageable way. With the above list of features (which I now have "hacked" into UIRect), it is far more extendable but again in a "hacked" fashion which gives me nightmares...
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 09, 2014, 08:41:14 PM
I agree that ideally it should be like that. Unfortunately the way panels work was set in stone many years ago, and even the eventual creation of UIRect in an attempt to optimize some of the common functionality was done years later. You can still use the common functionality via the UIRect's SetRect() function, set anchors the same way on both panels and widgets, and some other things, but there is a pretty big difference between panels and widgets that's not going to disappear due to backwards compatibility.

If I was to re-design it, throwing backwards compatibility to the wind, I'd do it differently. If I do NGUI for the Unreal Engine I will do just that. But this part of NGUI for Unity is not likely to change.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 14, 2014, 02:15:45 AM
I agree that ideally it should be like that. Unfortunately the way panels work was set in stone many years ago, and even the eventual creation of UIRect in an attempt to optimize some of the common functionality was done years later. You can still use the common functionality via the UIRect's SetRect() function, set anchors the same way on both panels and widgets, and some other things, but there is a pretty big difference between panels and widgets that's not going to disappear due to backwards compatibility.

If I was to re-design it, throwing backwards compatibility to the wind, I'd do it differently. If I do NGUI for the Unreal Engine I will do just that. But this part of NGUI for Unity is not likely to change.

That's really unfortunate. I sure hope the new UI-Engine for Unity 4.x doesn't get polluted with this, since as I understand NGUI has a vital part in that, yes? "Open for extension, closed for modification" is key in OOP (not trying to lecture you but more push on the importance).

Anyways, I noticed another very annoying issue with nested panels (A scrollview with a nested panel as child). Keeping the story short the issue lies in that UIRect is abstract and not the "true" base class for a UI frame, so I have to chose wether to have a UIPanel as the "rect", or a UIWidget. Problem is, I really need it to be a UIPanel and I naturally use the frame as bounds to know when cells should be put pack on the reusable stack (it's a custom made table with reusable cells to be able to have "infinite" cells in the table). Now, if the user wants the table can auto expand to fully enclose all cells, and in this case the bounds from the NEXT UIPanel with clipping turned on should be used to know if cells are visible or not.

So I have this (simplified for this example since this is the use case causing the bug):

- ScrollView A (with UIPanel attached)
-- Panel A
--- UIWidget (cell)
--- UIWidget (cell)
--- etc.

Now, if I activate cells when they become visible (using UIScrollViews bounds), they will be rendered in the wrong position (even though positioned correctly).

So I do this:

Since this only happens while ACTIVATING a widget inside a child panel while DRAGGING the parent-panel, one might conclude that the problem lies in the offset of ScrollView A's panel messing up the drawing of child-panel A.

That's a mouthful, but you should get the picture. To temporarily fix it, I added the possibility for UIPanel.Find to ignore panels if disabled and a boolean "IgnoreIfDisabled" is set to true. Thanks to this the panel being scrolled will also be responsible for drawing my cells, and the offset won't mess it up.

Thanks!
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 14, 2014, 06:49:42 AM
If it's what I think it is, then it should be fixed in 3.6.0. You can fix it locally by modifying UIPanel's clipOffset to this:
  1.         public Vector2 clipOffset
  2.         {
  3.                 get
  4.                 {
  5.                         return mClipOffset;
  6.                 }
  7.                 set
  8.                 {
  9.                         if (Mathf.Abs(mClipOffset.x - value.x) > 0.001f ||
  10.                                 Mathf.Abs(mClipOffset.y - value.y) > 0.001f)
  11.                         {
  12.                                 mClipOffset = value;
  13.                                 InvalidateClipping();
  14.  
  15.                                 // Call the event delegate
  16.                                 if (onClipMove != null) onClipMove(this);
  17. #if UNITY_EDITOR
  18.                                 if (!Application.isPlaying) UpdateDrawCalls();
  19. #endif
  20.                         }
  21.                 }
  22.         }
  23.  
  24.         void InvalidateClipping ()
  25.         {
  26.                 mResized = true;
  27.                 mMatrixFrame = -1;
  28.                 mCullTime = (mCullTime == 0f) ? 0.001f : RealTime.time + 0.15f;
  29.  
  30.                 for (int i = 0; i < list.size; ++i)
  31.                 {
  32.                         UIPanel p = list[i];
  33.                         if (p != this && p.parentPanel == this)
  34.                                 p.InvalidateClipping();
  35.                 }
  36.         }
...and changing UIPanel.IsVisible(UIWidget) to this:
  1.         public bool IsVisible (UIWidget w)
  2.         {
  3.                 if ((mClipping == UIDrawCall.Clipping.None || mClipping == UIDrawCall.Clipping.ConstrainButDontClip) && !w.hideIfOffScreen)
  4.                 {
  5.                         if (clipCount == 0) return true;
  6.                         if (mParentPanel != null) return mParentPanel.IsVisible(w);
  7.                 }
  8.  
  9.                 UIPanel p = this;
  10.                 Vector3[] corners = w.worldCorners;
  11.  
  12.                 while (p != null)
  13.                 {
  14.                         if (!IsVisible(corners[0], corners[1], corners[2], corners[3])) return false;
  15.                         p = p.mParentPanel;
  16.                 }
  17.                 return true;
  18.         }
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 14, 2014, 10:38:31 AM
If it's what I think it is, then it should be fixed in 3.6.0. You can fix it locally by modifying UIPanel's clipOffset to this:
  1.         public Vector2 clipOffset
  2.         {
  3.                 get
  4.                 {
  5.                         return mClipOffset;
  6.                 }
  7.                 set
  8.                 {
  9.                         if (Mathf.Abs(mClipOffset.x - value.x) > 0.001f ||
  10.                                 Mathf.Abs(mClipOffset.y - value.y) > 0.001f)
  11.                         {
  12.                                 mClipOffset = value;
  13.                                 InvalidateClipping();
  14.  
  15.                                 // Call the event delegate
  16.                                 if (onClipMove != null) onClipMove(this);
  17. #if UNITY_EDITOR
  18.                                 if (!Application.isPlaying) UpdateDrawCalls();
  19. #endif
  20.                         }
  21.                 }
  22.         }
  23.  
  24.         void InvalidateClipping ()
  25.         {
  26.                 mResized = true;
  27.                 mMatrixFrame = -1;
  28.                 mCullTime = (mCullTime == 0f) ? 0.001f : RealTime.time + 0.15f;
  29.  
  30.                 for (int i = 0; i < list.size; ++i)
  31.                 {
  32.                         UIPanel p = list[i];
  33.                         if (p != this && p.parentPanel == this)
  34.                                 p.InvalidateClipping();
  35.                 }
  36.         }
...and changing UIPanel.IsVisible(UIWidget) to this:
  1.         public bool IsVisible (UIWidget w)
  2.         {
  3.                 if ((mClipping == UIDrawCall.Clipping.None || mClipping == UIDrawCall.Clipping.ConstrainButDontClip) && !w.hideIfOffScreen)
  4.                 {
  5.                         if (clipCount == 0) return true;
  6.                         if (mParentPanel != null) return mParentPanel.IsVisible(w);
  7.                 }
  8.  
  9.                 UIPanel p = this;
  10.                 Vector3[] corners = w.worldCorners;
  11.  
  12.                 while (p != null)
  13.                 {
  14.                         if (!IsVisible(corners[0], corners[1], corners[2], corners[3])) return false;
  15.                         p = p.mParentPanel;
  16.                 }
  17.                 return true;
  18.         }

That seemed to do the trick indeed! Being too lazy to check, how much does this impact performance?

TY.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 14, 2014, 11:26:35 PM
It shouldn't impact it much. If anything, it's now at the performance level it should have been. Before it was skipping some important checks. It would be nearly the same as having just a single scroll view in terms of performance.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 15, 2014, 01:40:14 AM
It shouldn't impact it much. If anything, it's now at the performance level it should have been. Before it was skipping some important checks. It would be nearly the same as having just a single scroll view in terms of performance.

Alright.

About the next update, 3.6.0, could you make sure that using UIPanel & UIWidget through UIRect is transparent? I remember you added some more common functionality in UIRect (SetRect?), but just making sure you also add setters for width/height for UIPanel, pivotOffset (even if it's a constant, but it's definitely usable to have it customisable IMO), depth... Is what I can think of now. Everything that UIWidget & UIPanel have in common that makes sense (but as of now implemented differently) should be inherited from UIRect. So I don't have to mod it in myself, since then updating is always a fear... :P
I'll give you a golden star if you add a "onRectChanged" callback that is guaranteed to be invoked whenever any widget changes size. As of now, I mostly use MarkAsChanged to do this (to avoid polling), but with labels that is not 100% accurate (not getting invoked when typing new line etc.).

Thanks!

Edit. Center! You should be able to set/get center-position of UIRects.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 15, 2014, 11:31:40 AM
  1. Vector3[] corners = widget.worldCorners; // or localCorners
  2. Vector3 center = Vector3.Lerp(corners[0], corners[2], 0.5f);
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 15, 2014, 01:03:01 PM
  1. Vector3[] corners = widget.worldCorners; // or localCorners
  2. Vector3 center = Vector3.Lerp(corners[0], corners[2], 0.5f);

See, two lines just for getting the center. Now imagine doing that over and over... That's the point with convenience methods: They make things more convenient! :)
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: ArenMook on May 16, 2014, 12:49:31 PM
Well, you know, if the widget is using a center pivot, then its transform's position is its center. I can certainly add a method for it, but it still seems... unnecessary.
Title: Re: BUG(?): Dynamic Font in ScrollView, odd positioning
Post by: helmesjo on May 17, 2014, 01:11:59 AM
Well, you know, if the widget is using a center pivot, then its transform's position is its center. I can certainly add a method for it, but it still seems... unnecessary.

Nah, I don't agree. There is a lot of times where you need to position widgets centered in a line without caring about pivot-point. Pivot-point might differ because of user-preferences. Say if I write a special table-class used by others and they want a different pivot-point than the one I'm expecting, then there is no way for me to allow that without things getting messed up.