Author Topic: Keeping UI widget inside parent's edges  (Read 3148 times)

justkevin

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 8
    • View Profile
Keeping UI widget inside parent's edges
« on: June 30, 2014, 12:38:42 PM »
I'm working on a top down game in which there are some NGUI "scanner" widgets that stay over their respective game objects, with the following code on an ngui overlay that's anchored to the edges of the screen:

  1.        
  2. private void PositionScannable(IScannable scannable) {
  3.                 // Find the game object's screen position:
  4.                 Vector2 screenPos = Camera.main.WorldToScreenPoint(scannable.GameObject.transform.position);
  5.                 // Get the Camera rendering the overlay layer:
  6.                 Camera cam = NGUITools.FindCameraForLayer(gameObject.layer);
  7.  
  8.                 // Translate the screen pos into the coordinates for the NGUI camera:
  9.                 Vector3 adjustedPos = cam.ScreenToWorldPoint(screenPos);
  10.                 // ??? Somehow constrain to this widget's edges ???
  11.                 scannable.ScannerInstance.transform.position = adjustedPos;
  12.         }
  13.  

I want to keep the scanner widgets constrained to the overlay rectangle so they don't disappear off the edge of the screen when the object they're tracking does.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Keeping UI widget inside parent's edges
« Reply #1 on: July 01, 2014, 04:18:22 AM »
Use NGUIMath.ConstrainRect. To get the bounds of a widget, use NGUIMath.CalculateRelativeWidgetBounds.

justkevin

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 8
    • View Profile
Re: Keeping UI widget inside parent's edges
« Reply #2 on: July 01, 2014, 11:49:29 AM »
Thanks for the reply, I still can't seem to get this to work. The content either isn't adjusted at all, or adjusting itself to be half way between the center of the screen and whatever the target its tracking is (so eventually going off screen), depending on what I use as the "relative to" in the CalculateRelativeWidgetBounds.

  1.         private void PositionScannable(IScannable scannable) {
  2.                 // Find the game object's screen position:
  3.                 Vector2 screenPos = Camera.main.WorldToScreenPoint(scannable.GameObject.transform.position);
  4.                 // Get the Camera rendering the overlay layer:
  5.                 Camera cam = NGUITools.FindCameraForLayer(gameObject.layer);
  6.  
  7.                 // Translate the screen pos into the coordinates for the NGUI camera:
  8.                 Vector3 adjustedPos = cam.ScreenToWorldPoint(screenPos);
  9.  
  10.                 scannable.ScannerInstance.transform.position = adjustedPos;
  11.                 // At this point, if nothing else is done, the ScannerInstance follows the target, but can leave the area of the
  12.                 // this widget, which I don't want.
  13.  
  14.                 // Tried a number of variations of the following:
  15.                 /*
  16.                 Bounds contentBounds = NGUIMath.CalculateRelativeWidgetBounds(transform, scannable.ScannerInstance.transform);
  17.                 Vector2 scanMin = new Vector2(contentBounds.min.x, contentBounds.min.y);
  18.                 Vector2 scanMax = new Vector2(contentBounds.max.x, contentBounds.max.y);
  19.                 Bounds areaBounds = NGUIMath.CalculateRelativeWidgetBounds(transform);
  20.                 Vector2 areaMin = new Vector2(areaBounds.min.x, areaBounds.min.y);
  21.                 Vector2 areaMax = new Vector2(areaBounds.max.x, areaBounds.max.y);
  22.  
  23.                 Vector2 offset = NGUIMath.ConstrainRect(scanMin,scanMax,areaMin,areaMax);
  24.  
  25.                 Vector3 constrained = scannable.ScannerInstance.transform.position;
  26.                 constrained.x += offset.x;
  27.                 constrained.y += offset.y;
  28.  
  29.                 scannable.ScannerInstance.transform.position = cam.ScreenToViewportPoint(constrained);
  30.                 */
  31.         }

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Keeping UI widget inside parent's edges
« Reply #3 on: July 02, 2014, 03:55:10 AM »
The "relative to" parameter needs to be a common parent to the object you're moving, like so:

Common Parent
- Container Object
- Constained Object

Note how both are siblings in this case? If something is "eventually" going off-screen then it's most likely due to your hierarchy. Calculating the game object's bounds includes all of its children as well, so if you happen to move a child using the parent's bounds... those bounds will include that child too, which is not what you want.

justkevin

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 8
    • View Profile
Re: Keeping UI widget inside parent's edges
« Reply #4 on: July 02, 2014, 11:01:44 AM »
Thanks, that helped me get it working!

If anyone else searches for this, here is a working version, in which scannable.ScannerInstance is a UIWidget constrained to the boundaries of whatever UIWidget this component is attached:

  1. private void PositionScannable(IScannable scannable) {
  2.                 // Find the scannable game object's screen position:
  3.                 Vector2 screenPos = Camera.main.WorldToScreenPoint(scannable.GameObject.transform.position);
  4.                 // Get the Camera rendering the overlay layer:
  5.                 Camera cam = NGUITools.FindCameraForLayer(gameObject.layer);
  6.  
  7.                 // Translate the screen pos into the coordinates for the NGUI camera:
  8.                 Vector3 adjustedPos = cam.ScreenToWorldPoint(screenPos);
  9.  
  10.                 scannable.ScannerInstance.transform.position = adjustedPos;
  11.                 // At this point the scanner widget is following the game object, now to constrain it to this widget:
  12.  
  13.                 // Figure out the bounds relative to our mutual parent:
  14.                 Bounds contentBounds = NGUIMath.CalculateRelativeWidgetBounds(transform.parent, scannable.ScannerInstance.transform);
  15.                 Vector2 scanMin = new Vector2(contentBounds.min.x, contentBounds.min.y);
  16.                 Vector2 scanMax = new Vector2(contentBounds.max.x, contentBounds.max.y);
  17.                 Bounds areaBounds = NGUIMath.CalculateRelativeWidgetBounds(transform.parent, transform);
  18.                 Vector2 areaMin = new Vector2(areaBounds.min.x, areaBounds.min.y);
  19.                 Vector2 areaMax = new Vector2(areaBounds.max.x, areaBounds.max.y);
  20.                 // Get the offset in screen space, the amount we need to add to the position to constrain it:
  21.                 Vector2 offset = NGUIMath.ConstrainRect(scanMin,scanMax,areaMin,areaMax);
  22.                 // Re-calculate the screen coordinates of the scanner widget and adjust:
  23.                 screenPos = Camera.main.WorldToScreenPoint(scannable.GameObject.transform.position);
  24.                 screenPos.x += offset.x;
  25.                 screenPos.y += offset.y;
  26.                 // Translate back to world space and we should be good. There may be a simpler way to do this that doesn't
  27.                 // involve doing the camera translations twice, but right now this works.
  28.                 scannable.ScannerInstance.transform.position = cam.ScreenToWorldPoint(screenPos);
  29.  
  30.  
  31.         }
  32.