Author Topic: Recommended method for applying anchors to instantiated UI prefabs?  (Read 10737 times)

vexir

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 47
    • View Profile
Hi there,

A recommended implementation question for the forums - in the case where my UI is held mostly in prefabs that I instantiate and destroy on a need basis, what is the best way to make sure their anchors are properly set on instantiate? The anchors always go away because the prefabs can't reference an object in the scene, so it has to be done dynamically.

The best thing I can think of so far is saving off the anchors somewhere as well as the name of the target, and then re-applying that information on instantiate. This seems rather tedious however, so I'm wondering if there is a better way to do it?

Thanks!

pretender

  • Full Member
  • ***
  • Thank You
  • -Given: 1
  • -Receive: 0
  • Posts: 155
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #1 on: March 25, 2014, 03:07:02 PM »
I am interested in this as well!

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #2 on: March 26, 2014, 02:44:35 AM »
There is no magic trick with this. If you're instantiating something that should be anchored, it's up to you to then set the anchor via widget.SetAnchor or manually. If you're just trying to anchor them to the screen instead, just anchor them to UICamera.list[0].transform.

vexir

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 47
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #3 on: March 26, 2014, 10:46:28 AM »
Ah I see. I'd hoped NGUI had a helper class for this. I wrote my own, and it seems to work fairly well - it has a button to record the anchors in edit mode and then on Start(), it sets them back up by using the name of the target transform.

Perhaps you can help me with an issue with it - when it is instantiated and I restore the anchors, they seem slightly off for some reason. My suspicion is that they are anchoring before the parenting to UI Root is happening. My reasoning is such: I also have the custom editor restore the anchors OnEnable(), so that it is properly anchored when editing the prefab. However there is some quirky behavior: when I drag the prefab into the scene under UI Root, the anchors are restored correctly. When I drag the prefab into the scene at the top level, the prefab is automatically moved to be a child of UI Root, but the widget looks improperly anchored/sized even though it's values in the inspector are correct. This same incorrect sizing happens when I instantiate the prefab in the game, even though I am using NGUITools.AddChild to create it as a child of the UI Root.

Any idea why that might be happening?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #4 on: March 26, 2014, 07:45:43 PM »
Off the top of my head I can't tell you what's wrong, but I would advise doing your anchoring in Start(), not OnEnable().

vexir

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 47
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #5 on: April 01, 2014, 03:48:21 PM »
This is the script I'm using. You see that I'm calling the setup function in Start. For some reason, when I instantiate the prefab with this script on it, it acts funky. In the same way as if I were to drag the prefab into the scene NOT under UIRoot. If I drag the prefab directly in as a child of UI Root, it functions perfectly. That's why I'm confused - if I use AddChild to instantiate the prefab as a child of UIRoot, shouldn't that be doing the same thing as when I drag the prefab into the scene as a child of UI Root?

  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class SetupAnchors : MonoBehaviour {
  5.  
  6.     public UIWidget myWidget;
  7.  
  8.     public string leftAnchorTarget;
  9.     public string rightAnchorTarget;
  10.     public string topAnchorTarget;
  11.     public string bottomAnchorTarget;
  12.     public int leftAnchorValue;
  13.     public int rightAnchorValue;
  14.     public int topAnchorValue;
  15.     public int bottomAnchorValue;
  16.  
  17.     void Awake()
  18.     {
  19.  
  20.     }
  21.  
  22.     void Start()
  23.     {
  24.         SetupAnchorsForWidget();
  25.     }
  26.  
  27.     public void SetupAnchorsForWidget()
  28.     {
  29.         GameObject leftAnchorTargetObj = GameObject.Find(leftAnchorTarget);
  30.         if( leftAnchorTargetObj != null )
  31.         {
  32.             myWidget.leftAnchor.target = leftAnchorTargetObj.transform;
  33.             myWidget.leftAnchor.absolute = leftAnchorValue;
  34.         }
  35.  
  36.         GameObject rightAnchorTargetObj = GameObject.Find(rightAnchorTarget);
  37.         if( rightAnchorTargetObj != null )
  38.         {
  39.             myWidget.rightAnchor.target = rightAnchorTargetObj.transform;
  40.             myWidget.rightAnchor.absolute = rightAnchorValue;
  41.         }
  42.  
  43.         GameObject topAnchorTargetObj = GameObject.Find(topAnchorTarget);
  44.         if( topAnchorTargetObj != null )
  45.         {
  46.             myWidget.topAnchor.target = topAnchorTargetObj.transform;
  47.             myWidget.topAnchor.absolute = topAnchorValue;
  48.         }
  49.  
  50.         GameObject bottomAnchorTargetObj = GameObject.Find(bottomAnchorTarget);
  51.         if( bottomAnchorTargetObj != null )
  52.         {
  53.             myWidget.bottomAnchor.target = bottomAnchorTargetObj.transform;
  54.             myWidget.bottomAnchor.absolute = bottomAnchorValue;
  55.         }
  56.  
  57.         myWidget.ResetAnchors();
  58.         myWidget.UpdateAnchors();
  59.     }
  60.  
  61.     public void RecordAnchors()
  62.     {
  63.         if( myWidget == null )
  64.         {
  65.             myWidget = transform.GetComponent<UIWidget>();
  66.  
  67.             if( myWidget == null )
  68.             {
  69.                 Debug.Log("Error. You need a widget on this object.");
  70.             }
  71.         }
  72.  
  73.         if( myWidget.leftAnchor.target != null )
  74.         leftAnchorTarget = myWidget.leftAnchor.target.gameObject.name;
  75.  
  76.         if( myWidget.rightAnchor.target != null )
  77.         rightAnchorTarget = myWidget.rightAnchor.target.gameObject.name;
  78.  
  79.         if( myWidget.topAnchor.target != null )
  80.         topAnchorTarget = myWidget.topAnchor.target.gameObject.name;
  81.  
  82.         if( myWidget.bottomAnchor.target != null )
  83.         bottomAnchorTarget = myWidget.bottomAnchor.target.gameObject.name;
  84.  
  85.         if( myWidget.leftAnchor.target != null )
  86.         leftAnchorValue = myWidget.leftAnchor.absolute;
  87.  
  88.         if( myWidget.rightAnchor.target != null )
  89.         rightAnchorValue = myWidget.rightAnchor.absolute;
  90.  
  91.         if( myWidget.topAnchor.target != null )
  92.         topAnchorValue = myWidget.topAnchor.absolute;
  93.  
  94.         if( myWidget.bottomAnchor.target != null )
  95.         bottomAnchorValue = myWidget.bottomAnchor.absolute;
  96.  
  97.         Debug.Log("Anchors have been recorded.");
  98.     }
  99.        
  100.         // Update is called once per frame
  101.         void Update () {
  102.        
  103.         }
  104. }
« Last Edit: April 01, 2014, 06:05:36 PM by vexir »

vexir

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 47
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #6 on: April 01, 2014, 06:35:38 PM »
I should point out that upgrading to 3.5.5 did not help the problem and that using legacy anchors did fix it for me. I really like the new layout system, but the lack of support for instantiated prefabs is becoming difficult to deal with. Any help would be really appreciated  :D

vexir

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 47
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #7 on: April 02, 2014, 04:16:43 PM »
Bump.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #8 on: April 02, 2014, 05:58:49 PM »
GameObject.Find anything by name is terrible practice, and is the slowest operation in the world. What if you have two widgets with the same name? What if you rename something later without realizing it? Also note that you're setting the absolute anchor values here, but you're leaving relative ones alone, so by default it will be left to left, right to right, etc. I don't know what you had them as before.

vexir

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 47
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #9 on: April 02, 2014, 06:46:25 PM »
I added the relative values into the script. Still no bueno. Upon instantiation, the object shows up much larger and offset than it should.

  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class SetupAnchors : MonoBehaviour {
  5.  
  6.     public UIWidget myWidget;
  7.  
  8.     public string leftAnchorTarget;
  9.     public string rightAnchorTarget;
  10.     public string topAnchorTarget;
  11.     public string bottomAnchorTarget;
  12.     public int leftAnchorValue;
  13.     public int rightAnchorValue;
  14.     public int topAnchorValue;
  15.     public int bottomAnchorValue;
  16.     public float leftAnchorRelativeValue;
  17.     public float rightAnchorRelativeValue;
  18.     public float topAnchorRelativeValue;
  19.     public float bottomAnchorRelativeValue;
  20.  
  21.     void Awake()
  22.     {
  23.  
  24.     }
  25.  
  26.     void Start()
  27.     {
  28.         SetupAnchorsForWidget();
  29.     }
  30.  
  31.     public void SetupAnchorsForWidget()
  32.     {
  33.         GameObject leftAnchorTargetObj = GameObject.Find(leftAnchorTarget);
  34.         if( leftAnchorTargetObj != null )
  35.         {
  36.             myWidget.leftAnchor.target = leftAnchorTargetObj.transform;
  37.             myWidget.leftAnchor.absolute = leftAnchorValue;
  38.             myWidget.leftAnchor.relative = leftAnchorRelativeValue;
  39.         }
  40.  
  41.         GameObject rightAnchorTargetObj = GameObject.Find(rightAnchorTarget);
  42.         if( rightAnchorTargetObj != null )
  43.         {
  44.             myWidget.rightAnchor.target = rightAnchorTargetObj.transform;
  45.             myWidget.rightAnchor.absolute = rightAnchorValue;
  46.             myWidget.rightAnchor.relative = rightAnchorRelativeValue;
  47.         }
  48.  
  49.         GameObject topAnchorTargetObj = GameObject.Find(topAnchorTarget);
  50.         if( topAnchorTargetObj != null )
  51.         {
  52.             myWidget.topAnchor.target = topAnchorTargetObj.transform;
  53.             myWidget.topAnchor.absolute = topAnchorValue;
  54.             myWidget.topAnchor.relative = topAnchorRelativeValue;
  55.         }
  56.  
  57.         GameObject bottomAnchorTargetObj = GameObject.Find(bottomAnchorTarget);
  58.         if( bottomAnchorTargetObj != null )
  59.         {
  60.             myWidget.bottomAnchor.target = bottomAnchorTargetObj.transform;
  61.             myWidget.bottomAnchor.absolute = bottomAnchorValue;
  62.             myWidget.bottomAnchor.relative = bottomAnchorRelativeValue;
  63.         }
  64.  
  65.         myWidget.ResetAnchors();
  66.         myWidget.UpdateAnchors();
  67.     }
  68.    
  69.     public void RecordAnchors()
  70.     {
  71.         if( myWidget == null )
  72.         {
  73.             myWidget = transform.GetComponent<UIWidget>();
  74.  
  75.             if( myWidget == null )
  76.             {
  77.                 Debug.Log("Error. You need a widget on this object.");
  78.             }
  79.         }
  80.  
  81.         if( myWidget.leftAnchor.target != null )
  82.         {
  83.             leftAnchorTarget = myWidget.leftAnchor.target.gameObject.name;
  84.             leftAnchorValue = myWidget.leftAnchor.absolute;
  85.             leftAnchorRelativeValue = myWidget.leftAnchor.relative;
  86.         }
  87.  
  88.         if( myWidget.rightAnchor.target != null )
  89.         {
  90.             rightAnchorTarget = myWidget.rightAnchor.target.gameObject.name;
  91.             rightAnchorValue = myWidget.rightAnchor.absolute;
  92.             rightAnchorRelativeValue = myWidget.rightAnchor.relative;
  93.         }
  94.  
  95.         if( myWidget.topAnchor.target != null )
  96.         {
  97.             topAnchorTarget = myWidget.topAnchor.target.gameObject.name;
  98.             topAnchorValue = myWidget.topAnchor.absolute;
  99.             topAnchorRelativeValue = myWidget.topAnchor.relative;
  100.         }
  101.  
  102.         if( myWidget.bottomAnchor.target != null )
  103.         {
  104.             bottomAnchorTarget = myWidget.bottomAnchor.target.gameObject.name;
  105.             bottomAnchorValue = myWidget.bottomAnchor.absolute;
  106.             bottomAnchorRelativeValue = myWidget.bottomAnchor.relative;
  107.         }
  108.  
  109.         Debug.Log("Anchors have been recorded.");
  110.     }
  111.        
  112.         // Update is called once per frame
  113.         void Update () {
  114.        
  115.         }
  116. }
  117.  

In regards to GameObject.Find() - Yes, that's true; I try and avoid it if I can, but how else to make sure that the anchors are being setup with the right GameObject as their target? We're instantiating from a prefab, so without a name it's difficult to make sure that stays the same. It only happens once on Start(), so performance is not an issue. The naming would be though. Since the script isn't functioning correctly yet, I'll leave it as is and implement a cleaner bit after it's all operational.

Tripwire

  • Full Member
  • ***
  • Thank You
  • -Given: 9
  • -Receive: 0
  • Posts: 163
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #10 on: April 03, 2014, 02:12:30 AM »
Hmm i'm interested in this aswel, for the GameObject.Find part, if you use NGUITools.AddWidget or NGUITools.AddChild it returns a GameObject:
  1. GameObject Clone = NGUITools.AddWidget(Prefab);
  2.  
  3. SetupLeftAnchorWidget(Clone);
  4.  
  5. SetupLeftAnchorForWidget(GameObject obj)
  6. {
  7.        
  8. }
  9.  

This would take care of the GameObject.Find performance problem. Thing is, im trying this out, but when anchoring at runtime the widget doesn't really scale to the right proportions. For instance my prefab is for resolution width 640 but my screen when instantiating is 720. This would mean that the dimensions of the widget has to be set through script before anchoring (because when anchored you can't set the dimensions of the widget).

EDIT:
I just realized you can set the dimensions for an anchored widget with NGUIMath.ResizeWidget / NGUIMath.AdjustWidget

vexir

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 47
    • View Profile
Re: Recommended method for applying anchors to instantiated UI prefabs?
« Reply #11 on: April 03, 2014, 11:43:33 AM »
Okay, so I fixed it. Not only did I have to save off the width and height (I didn't before because I assumed the anchors would take care of them, looks like it's the other way around), but I had to save off the position and reapply it as well.

It seems that for some reason the position, upon instantiation with AddChild, was being set to the center of the screen. That, following the application of the anchors, was screwing up the dimensions.

Final functioning script (sorry about the GameObject.Find(), I'll have to deal with that later):

  1. public class SetupAnchors : MonoBehaviour {
  2.  
  3.     public UIWidget myWidget;
  4.  
  5.     public string leftAnchorTarget;
  6.     public string rightAnchorTarget;
  7.     public string topAnchorTarget;
  8.     public string bottomAnchorTarget;
  9.     public int leftAnchorValue;
  10.     public int rightAnchorValue;
  11.     public int topAnchorValue;
  12.     public int bottomAnchorValue;
  13.     public float leftAnchorRelativeValue;
  14.     public float rightAnchorRelativeValue;
  15.     public float topAnchorRelativeValue;
  16.     public float bottomAnchorRelativeValue;
  17.  
  18.     public int width;
  19.     public int height;
  20.  
  21.     public Vector3 localPos;
  22.  
  23.     void Awake()
  24.     {
  25.  
  26.     }
  27.  
  28.     void Start()
  29.     {
  30.         SetupAnchorsForWidget();
  31.     }
  32.  
  33.     public void SetupAnchorsForWidget()
  34.     {
  35.         transform.localPosition = localPos;
  36.  
  37.         myWidget.width = width;
  38.         myWidget.height = height;
  39.  
  40.         GameObject leftAnchorTargetObj = GameObject.Find(leftAnchorTarget);
  41.         if( leftAnchorTargetObj != null )
  42.         {
  43.             myWidget.leftAnchor.target = leftAnchorTargetObj.transform;
  44.             myWidget.leftAnchor.absolute = leftAnchorValue;
  45.             myWidget.leftAnchor.relative = leftAnchorRelativeValue;
  46.         }
  47.  
  48.         GameObject rightAnchorTargetObj = GameObject.Find(rightAnchorTarget);
  49.         if( rightAnchorTargetObj != null )
  50.         {
  51.             myWidget.rightAnchor.target = rightAnchorTargetObj.transform;
  52.             myWidget.rightAnchor.absolute = rightAnchorValue;
  53.             myWidget.rightAnchor.relative = rightAnchorRelativeValue;
  54.         }
  55.  
  56.         GameObject topAnchorTargetObj = GameObject.Find(topAnchorTarget);
  57.         if( topAnchorTargetObj != null )
  58.         {
  59.             myWidget.topAnchor.target = topAnchorTargetObj.transform;
  60.             myWidget.topAnchor.absolute = topAnchorValue;
  61.             myWidget.topAnchor.relative = topAnchorRelativeValue;
  62.         }
  63.  
  64.         GameObject bottomAnchorTargetObj = GameObject.Find(bottomAnchorTarget);
  65.         if( bottomAnchorTargetObj != null )
  66.         {
  67.             myWidget.bottomAnchor.target = bottomAnchorTargetObj.transform;
  68.             myWidget.bottomAnchor.absolute = bottomAnchorValue;
  69.             myWidget.bottomAnchor.relative = bottomAnchorRelativeValue;
  70.         }
  71.  
  72.         myWidget.ResetAnchors();
  73.         myWidget.UpdateAnchors();
  74.     }
  75.    
  76.     public void RecordAnchors()
  77.     {
  78.         localPos = transform.localPosition;
  79.  
  80.         if( myWidget == null )
  81.         {
  82.             myWidget = transform.GetComponent<UIWidget>();
  83.  
  84.             if( myWidget == null )
  85.             {
  86.                 Debug.Log("Error. You need a widget on this object.");
  87.             }
  88.         }
  89.  
  90.         if( myWidget.leftAnchor.target != null )
  91.         {
  92.             leftAnchorTarget = myWidget.leftAnchor.target.gameObject.name;
  93.             leftAnchorValue = myWidget.leftAnchor.absolute;
  94.             leftAnchorRelativeValue = myWidget.leftAnchor.relative;
  95.         }
  96.  
  97.         if( myWidget.rightAnchor.target != null )
  98.         {
  99.             rightAnchorTarget = myWidget.rightAnchor.target.gameObject.name;
  100.             rightAnchorValue = myWidget.rightAnchor.absolute;
  101.             rightAnchorRelativeValue = myWidget.rightAnchor.relative;
  102.         }
  103.  
  104.         if( myWidget.topAnchor.target != null )
  105.         {
  106.             topAnchorTarget = myWidget.topAnchor.target.gameObject.name;
  107.             topAnchorValue = myWidget.topAnchor.absolute;
  108.             topAnchorRelativeValue = myWidget.topAnchor.relative;
  109.         }
  110.  
  111.         if( myWidget.bottomAnchor.target != null )
  112.         {
  113.             bottomAnchorTarget = myWidget.bottomAnchor.target.gameObject.name;
  114.             bottomAnchorValue = myWidget.bottomAnchor.absolute;
  115.             bottomAnchorRelativeValue = myWidget.bottomAnchor.relative;
  116.         }
  117.  
  118.         width = myWidget.width;
  119.         height = myWidget.height;
  120.  
  121.         Debug.Log("Anchors have been recorded.");
  122.     }
  123.        
  124.         // Update is called once per frame
  125.         void Update () {
  126.        
  127.         }
  128. }
« Last Edit: April 03, 2014, 12:36:14 PM by vexir »