Author Topic: Proposing UIFill, UIStick and UIHook  (Read 9700 times)

toomas

  • Guest
Proposing UIFill, UIStick and UIHook
« on: April 24, 2012, 08:16:10 AM »
I am making user interface for quite a complicated tool which we are developing in Unity. We haven't publicly released anything yet, so I can't show it yet. But during the development I faced quite a few limitations of NGUI and had to make custom solutions to get things working as we needed. Since these are not our app specific, I would very much like to see these features in NGUI and if you think this approach I've taken is good enough, these files can be integrated and shipped with NGUI in the future. Of course, some editing needs to be done, because there are some limitations and imperfections, but it might be a starting point. For example it requires all components and panels to have TopLeft pivots.

I propose 3 components:
- UIFill, which will automatically resize your panels and components to a size of container. The container can be screen, panel or another component.
- UIStick will take your component or panel and will make sure it sticks to an edge or corner of container. Again, container can be screen, panel or another component.
- UIHook will link some component or panels parameters together. For example when resizing one panel larger, another one will automatically get smaller

I uploaded a demo scene for your comments. I would rather give this code away and ideally see it integrated in NGUI, than starting to maintain it ourself. All I ask for is that I get credited when it goes into use somewhere :)

http://fanchinima.com/dev/NGuiExpFeatures.unitypackage (790k)

joelsantos

  • Guest
Re: Proposing UIFill, UIStick and UIHook
« Reply #1 on: April 24, 2012, 02:00:21 PM »
This should definitely be included. Specially  the UIFill. Nice work

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Proposing UIFill, UIStick and UIHook
« Reply #2 on: April 24, 2012, 02:43:06 PM »
That's pretty cool. Looking at your demo scene I noticed a bug I left behind in UIDragObject related to its momentum. Time for another micro-update... <_<

Alahmnat

  • Guest
Re: Proposing UIFill, UIStick and UIHook
« Reply #3 on: June 25, 2012, 12:36:15 AM »
UIStick has been quite helpful for me, but I was having some serious problems getting it to work with just the TopLeft pivot point supported. So, I updated the position-setting code to support placing the parent widget onto any of the 9 available pivot points, and widgets using UIStick will place themselves in the right spot. I thought I'd share my changes back in the event that someone else may get some use out of it, and because I'm sure I did something wrong somewhere ;).

Most importantly, this may not properly position stuff on panels if they have a non-zero pivot offset by default, since I only change the newly-declared (0, 0) offset if the widget is being attached to an actual sprite.

  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. /**
  5.  * Makes NGUI object or panel stick to an edge or corner of a "container"
  6.  *
  7.  * Usage:
  8.  *  1. attach to NGUI object or Panel that you want stick somewhere
  9.  *  2. attach container (when not set, Screen is used)
  10.  *
  11.  * NB! Right now it only works with containers that have pivot in top left corner.
  12.  * When using default NGUI panel, use UIPanelAutoSetCenter on panel to do that
  13.  * automatically.
  14.  */
  15. [ExecuteInEditMode]
  16. public class UIStick : MonoBehaviour {
  17.        
  18.         /// <summary>
  19.         /// The container.
  20.         /// </summary>
  21.         public Transform container;
  22.        
  23.         /// <summary>
  24.         /// On which side of the container this element should stick to.
  25.         /// </summary>
  26.         public UIAnchor.Side side;
  27.        
  28.         /// <summary>
  29.         /// when side is Top or Bottom, then should the object be horizontally centered?
  30.         /// when side is Left or Right, then should the object be vertically centered?
  31.         /// when side is Center, then this is not relevant
  32.         /// </summary>
  33.         public bool forceCenter = false;
  34.        
  35.         public bool alterSizeInsteadOfPosition;
  36.        
  37.         /// <summary>
  38.         /// The padding.
  39.         /// </summary>
  40.         public RectOffset padding;
  41.        
  42.         void Update ()
  43.         {
  44.                 Rect stickTo = new Rect(padding.left, padding.top,
  45.                                         Screen.width - padding.horizontal,
  46.                                         Screen.height - padding.vertical);
  47.                
  48.                 Vector2 pivotOffset = new Vector2(0f, 0f);
  49.                
  50.                 // when no container is given, stick to screen edges
  51.                 // otherwise stick to container
  52.                 if(container != null)
  53.                 {
  54.                         UIPanel panel = container.GetComponent<UIPanel>();
  55.                        
  56.                         // UIPanel is used as container
  57.                         if(panel != null)
  58.                         {
  59.                                 if(panel.clipping == UIDrawCall.Clipping.None) {
  60.                                         Debug.LogWarning("Container UIPanel has no clipping set. UIStick is not used.");
  61.                                         return;
  62.                                 }
  63.                                 stickTo.x = padding.left;
  64.                                 stickTo.y = padding.top;
  65.                                 stickTo.width = panel.clipRange[2] - padding.horizontal;
  66.                                 stickTo.height = panel.clipRange[3] - padding.vertical;
  67.                         }
  68.                         // NGUI object (size=gameobject.localScale) is used as container
  69.                         else {
  70.                                 UISprite sprite = container.GetComponent<UISprite>();
  71.                                 if(sprite != null) {
  72.                                         pivotOffset = sprite.pivotOffset;
  73.                                 }
  74.                                
  75.                                 stickTo.x = container.localPosition.x + padding.left;
  76.                                 stickTo.y = container.localPosition.y + padding.top;
  77.                                 stickTo.width = container.localScale.x - padding.horizontal;
  78.                                 stickTo.height = container.localScale.y - padding.vertical;
  79.                         }
  80.                 }
  81.                
  82.                 // UIPanel is that needs to be moved around
  83.                 UIPanel thisPanel = GetComponent<UIPanel>();
  84.                 Vector2 newPos;
  85.                 if(thisPanel != null)
  86.                 {
  87.                         if(thisPanel.clipping == UIDrawCall.Clipping.None) {
  88.                                 Debug.LogWarning("Space filling UIPanel has no clipping set. UIStick is not used.");
  89.                                 return;
  90.                         }
  91.                        
  92.                         Vector2 thisPanelSize = new Vector2(thisPanel.clipRange[2], thisPanel.clipRange[3]);
  93.                         newPos = GetContentPosition(stickTo, thisPanelSize, side, pivotOffset);
  94.                 }
  95.                 // NGUI object (size=gameobject.localScale) is that needs to be moved
  96.                 else {
  97.                         newPos = GetContentPosition(stickTo, transform.localScale, side, pivotOffset);
  98.                 }
  99.                
  100.                 if(!forceCenter) {
  101.                         // keep horizontal position when side is Top or Bottom
  102.                         if(side == UIAnchor.Side.Top || side == UIAnchor.Side.Bottom) {
  103.                                 newPos.x = transform.localPosition.x;
  104.                         }
  105.                         // keep vertical position when side is Left or Right
  106.                         if(side == UIAnchor.Side.Left || side == UIAnchor.Side.Right) {
  107.                                 newPos.y = transform.localPosition.y;
  108.                         }
  109.                 }
  110.                
  111.                 if(!alterSizeInsteadOfPosition)
  112.                 {
  113.                         transform.localPosition = new Vector3(newPos.x, newPos.y, transform.localPosition.z);
  114.                 } else {
  115.                         thisPanel.clipRange = new Vector4(thisPanel.clipRange[0],
  116.                                                           thisPanel.clipRange[1],
  117.                                                           thisPanel.clipRange[2] + (newPos.x - transform.localPosition.x),
  118.                                                           thisPanel.clipRange[3] + (newPos.y - transform.localPosition.y));
  119.                 }
  120.         }
  121.        
  122.         private static Vector2 GetContentPosition(Rect stickTo, Vector2 contentSize, UIAnchor.Side side, Vector2 pivotOffset)
  123.         {
  124.                 Vector2 p = Vector2.zero;
  125.                
  126.                 // horizontal position
  127.                 switch(side)
  128.                 {
  129.                         case UIAnchor.Side.BottomLeft:
  130.                         case UIAnchor.Side.Left:
  131.                         case UIAnchor.Side.TopLeft:
  132.                                 p.x = stickTo.x + (stickTo.width * pivotOffset.x);
  133.                                 break;
  134.                        
  135.                         case UIAnchor.Side.Bottom:
  136.                         case UIAnchor.Side.Center:
  137.                         case UIAnchor.Side.Top:
  138.                                 p.x = 0.5f * (stickTo.x * 2 + stickTo.width - contentSize.x) + (stickTo.width * pivotOffset.x);
  139.                                 break;
  140.                        
  141.                         case UIAnchor.Side.BottomRight:
  142.                         case UIAnchor.Side.Right:
  143.                         case UIAnchor.Side.TopRight:
  144.                                 p.x = (stickTo.x + stickTo.width - contentSize.x) + (stickTo.width * pivotOffset.x);
  145.                                 break;
  146.                 }
  147.        
  148.                 // vertical orientation
  149.                 switch(side)
  150.                 {      
  151.                         case UIAnchor.Side.TopLeft:
  152.                         case UIAnchor.Side.Top:
  153.                         case UIAnchor.Side.TopRight:
  154.                                 p.y = -stickTo.y + (stickTo.height * pivotOffset.y);
  155.                                 break;
  156.                                
  157.                         case UIAnchor.Side.Right:
  158.                         case UIAnchor.Side.Center:
  159.                         case UIAnchor.Side.Left:
  160.                                 p.y = -0.5f * (stickTo.height - contentSize.y) + (stickTo.height * pivotOffset.y) + stickTo.y;
  161.                                 break;
  162.                        
  163.                         case UIAnchor.Side.BottomRight:
  164.                         case UIAnchor.Side.Bottom:
  165.                         case UIAnchor.Side.BottomLeft:
  166.                                 p.y = -(stickTo.y + stickTo.height - contentSize.y) + (stickTo.height * pivotOffset.y);
  167.                                 break;
  168.                 }
  169.                 return p;
  170.         }
  171. }
  172.  

Thanks, toomas, for doing all of the heavy lifting :).
« Last Edit: June 25, 2012, 12:46:26 AM by Alahmnat »

Nicki

  • Global Moderator
  • Hero Member
  • *****
  • Thank You
  • -Given: 33
  • -Receive: 141
  • Posts: 1,768
    • View Profile
Re: Proposing UIFill, UIStick and UIHook
« Reply #4 on: June 25, 2012, 02:25:53 AM »
This is fucking brilliant. :D
ArenMook, you should include this directly in NGUI when you have time - it's an excellent feature that few people otherwise will find.

mdeletrain

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 71
    • View Profile
Re: Proposing UIFill, UIStick and UIHook
« Reply #5 on: June 25, 2012, 04:24:47 AM »
Pretty good components indeed !

Talking about layout components I've looked at the UIAnchor one and it seems it should be fairly easy to improve the feature a lot : being able to specify UIWIdgets as well as UICamera as anchor target.
Currently, the code gets the bounding rectangle from the associated camera, but it could retrieve it from an UIWidget as well, albeit not in the same manner.
This could prove very useful when dealing with complex UIs as a hierarchy could then be created between components.

PixelEnvision

  • Newbie
  • *
  • Thank You
  • -Given: 1
  • -Receive: 0
  • Posts: 35
    • View Profile
    • Pixel Envision
Re: Proposing UIFill, UIStick and UIHook
« Reply #6 on: June 25, 2012, 07:14:00 PM »
Looks very useful, thx for sharing this... 8)
Pixel Envision - Creating fun apps for iOS & Android

Twitter / Facebook / YouTube

n8

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 23
    • View Profile
Re: Proposing UIFill, UIStick and UIHook
« Reply #7 on: June 26, 2012, 11:38:44 AM »
+1 for adding these to NGUI

PiWizard

  • Guest
Re: Proposing UIFill, UIStick and UIHook
« Reply #8 on: July 06, 2012, 05:44:17 PM »
The UIStick is something I've been looking at. Currently the anchor doesn't always like to stick to the edge and I specifically want things to stick to the edge.

Thanks

gekido

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 18
    • View Profile
Not compatible with latest version of NGUI?
« Reply #9 on: July 24, 2012, 10:44:38 PM »
This package is no longer compatible with NGUI unfortunately. Not only that, but it overwrites key NGUI files and breaks the library to the point that you have to revert the entire library in order to get it working again.

Too bad, I wish these components would become 'core' components (particularly the stretch), because when dealing with random resolutions on mobile, these become very useful.

Just a warning to everyone.

Malzbier

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 93
    • View Profile
Re: Proposing UIFill, UIStick and UIHook
« Reply #10 on: July 25, 2012, 07:50:40 AM »
@gekido: Thanks for the warning.

@ArenMook: Is there a way for you to integrate thous components into NGUI?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Proposing UIFill, UIStick and UIHook
« Reply #11 on: July 25, 2012, 09:17:06 AM »
These exact ones? Nope. Something similar -- possibly.

anlumo

  • Guest
Re: Proposing UIFill, UIStick and UIHook
« Reply #12 on: January 17, 2013, 02:01:04 PM »
These exact ones? Nope. Something similar -- possibly.

Any updates on this? I've looked for about two hours now, and cannot find anything. There's UIAnchor and UIStretch, but those are only relative to the camera, not relative to a panel.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Proposing UIFill, UIStick and UIHook
« Reply #13 on: January 17, 2013, 09:04:04 PM »
UIAnchor and UIStretch both work with widgets now, not just the camera. You need a full version of NGUI.

anlumo

  • Guest
Re: Proposing UIFill, UIStick and UIHook
« Reply #14 on: January 17, 2013, 11:24:24 PM »
UIAnchor and UIStretch both work with widgets now, not just the camera. You need a full version of NGUI.

Thank you for the quick response. I experimented around with them, now that I know that I'm on the right track. However, they don't quite do everything I need them to do.

For example, I'm having a hard time to get it working with a scroll view (using the clipping panel method). The UIPanel soft clip setting requires hard coded values, which I can't provide.

Adjusting the UIGrid's number of items based on the width was rather easy using a custom script, though.