Search Unity

Scrollrect & Runtime content

Discussion in 'UGUI & TextMesh Pro' started by Winklehner, Aug 22, 2014.

  1. Winklehner

    Winklehner

    Joined:
    Apr 12, 2013
    Posts:
    20
    Im trying to fill in content for a scroll rect during runtime. First I would be interested in what is best practice to do so? As currently Im instantiating a prefab, setting the parent to the content rect and adjusting the position of the item, as well as the size of the content rect:

    Code (CSharp):
    1. for (int i=0; i<List.Rows.Count; i++)
    2.  
    3.             GameObject rect = (GameObject)Instantiate(itemPrefab, new Vector3(pos,0,0), Quaternion.identity);
    4.             RectTransform rectTransform = rect.GetComponent<RectTransform>();
    5.             rectTransform.parent = content;
    6.             pos += 100;
    7.             content.height += 100;
    8. ...
    One odd thing i noticed, as im using the Reference Resolution script on the canvas, the runtime instantiated Prefab is not affected by this so it will not fit if screen res != reference resolution.
    [Added code tags - Ed.]
     
    Last edited by a moderator: Dec 12, 2014
  2. DESTRUKTORR

    DESTRUKTORR

    Joined:
    Jul 4, 2012
    Posts:
    22
    It may be a bit off topic from your request, but RectTransform directly inherits from Transform. You should take advantage of the Transform property in the Monobehaviour and cast it rather than using a GetComponent call to get its reference.

    Also, there is no height property/field for Transform or RectTransform, so I'm not really sure how your code doesn't produce an error.
     
  3. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    i am having all manner of totally mind blowing weird things happen when I try to do this...

    First off, I do this:
    Code (csharp):
    1.  
    2. public RectTransform ScrollRegion;
    3. void Start()
    4. {
    5.     ScrollRegion.rect.Set(5, 5, 5, 5);
    6. }
    7.  
    As noted aove, the size remains unchanged at PosX 0, PosY 0, PosZ 0, Width 1700 and Height 166

    Next, I print the localPosition and position of the RectTransform set as the Content for the ScrollRect. It's coordinates are the ones I mentioned above... 0,0,0 and 1700, 166 and the anchors are set to stretch vertically and anchor to the left. Anchors min 0 and 0.5, max 0 and 0.5, pivot 0 and 0.5... So why in the world does the localPosition print off as -468,0,0 and the position as 279.8, 111.7.0 ???

    Where the hell does that -468 and 279 come from???

    But even more interestingly... I have an Image component with a bunch of child objects that I created a prefab from. When I instantiate it, I do this:
    Code (csharp):
    1.  
    2.     float this_offset = GetComponent<RectTransform>().localPosition.x;
    3.     buttons = new WUNLockableButton[count];
    4.     for( int i = 0; i < count; i++)
    5.     {
    6.         buttons[i] = (WUNLockableButton)Instantiate(button_prefab);
    7.         buttons[i].transform.parent = transform;
    8.         buttons[i].transform.GetComponent<RectTransform>().position = new Vector3((i * 300f) - this_offset, 0f, 0f);
    9.     }
    10.  
    Notice how I already detract the parent's x position from the local position of the child object as , by default, the localposition is wayyyyyyy far to the right... Now keep in mind I set the local position to 0 then detract a value from it and then notice how I start i with a value of 0 and each increment I add 300 to the x.... So someone please explain to me why the first object created has this RectTransform values:
    Screen Shot 2014-08-22 at 4.21.29 PM.png
    After that, each button loads 300 units apart as expected but why in the world does the first one have an offset of 188 when I clearly say the position.x must be 0 ???
    When I change that code to set the localPosition, not the position, I get this
    Screen Shot 2014-08-22 at 4.23.55 PM.png
    Mind you, when setting localPosition, the Top and Bottom are both set to 83 but because the image gets stretched, that make absolutely no difference to the final image and when I change that to 0 and 0 the image looks identical so even though I have no idea why it is set to 83 in the first place, at least it works...

    On the other hand, when i set the position, the Top becomes 194 and the bottom becomes -28 resulting in most of the image being clipped.

    Why ? what is going on here????

    How am I supposed to position RectTransform objects under parents without this kind of magic numbers appearing out of thin air?
     
    Last edited: Aug 22, 2014
  4. Winklehner

    Winklehner

    Joined:
    Apr 12, 2013
    Posts:
    20
    content.height += 100; was just a shortcut for:

    Vector2 sizeDelta = content.sizeDelta;
    sizeDelta.y += 100;
    content.sizeDelta = sizeDelta;

    This way I have no problems increasing the scroll rect content size. I also tried content.rect.Set(...) but this was not working for me. I guess its calculated and should not be set there.

    Still the prefabs fonts and images are huge, as they don't scale by Reference Resolution script (i guess).
     
  5. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I disagree with this. The Transform property no longer exists in Unity 5 and we need to get the Transform using the GetComponent method so it makes sense to get into the habit of using that now and never again make use of the Transform property

    EDIT: I researched this statement and I am wrong.
     
    Last edited: Aug 22, 2014
  6. Winklehner

    Winklehner

    Joined:
    Apr 12, 2013
    Posts:
    20
    The error seems to come from:

    RectTransform prefab = Instantiate....

    prefab.SetParent(content); // Now Unity puts 2,2,2 into Scale of the prefab rect transform????

    prefab.localScale = new Vector3(1,1,1); // fixes it

    Is this a bug or do I forgot something?
     
  7. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I actually got mine to work just fine also (all except resizing the scrollview width itself...)

    When I created my scrollarea, I resized the area, not scaled it. I find life a whole lot easier when I leave stuff scaled to 1. Then it was just a matter of setting the prefab's pivot AT RUNTIME and adjusting anchoredPosition instead of localPosition, and Bob's your uncle. Check it out...
    Code (csharp):
    1.         public void CreateButtons(int count)
    2.         {
    3.             Rect sample_size = button_prefab.transform.Find("Action").GetComponent<RectTransform>().rect;
    4.  
    5.             float spacing = sample_size.width * 0.05f;
    6.             float scroll_region_width = (count * (sample_size.width + spacing)) - spacing;
    7.  
    8.             ScrollRegion.rect.Set(0,0, scroll_region_width, 166f );
    9.  
    10.             Debug.Log (sample_size.width);
    11.             buttons = new WUNLockableButton[count];
    12.             for( int i = 0; i < count; i++)
    13.             {
    14.                 buttons[i] = (WUNLockableButton)Instantiate(button_prefab);
    15.                 buttons[i].transform.parent = transform;
    16.                 buttons[i].transform.GetComponent<RectTransform>().pivot = new Vector2(0, 0.5f);
    17.                 buttons[i].transform.GetComponent<RectTransform>().anchoredPosition =  new Vector2( (sample_size.width/2f) + (i * 300f), 0);
    18.             }
    19.         }
    20.  
    That works perfectly for horizontal scroll regions. All except for
    Code (csharp):
    1.  
    2.     ScrollRegion.rect.Set(0,0, scroll_region_width, 166f );
    3.  
    *NOTE: Find("Action"). . . That is just me finding one of the two images contained in my prefab and using it's size for the prefab size. Feel free to remove that from your code.

    Also, from reading your sample it seems your scrollarea might be scaled. Just check and make sure it it set to scale 1 for everything and if that messes up your size, use the resize tool to resize it, not the scale tool. Perhaps that will solve your issue right there...
     
    Last edited: Aug 22, 2014
  8. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Do you have a source for this? I know that they are getting rid of many of the convenience accessors (.rigidbody, etc), but I thought that .gameObject and .transform were two that they were keeping, since every single object will have both of those things (and there's no reason for .transform to use GetComponent<Transform>(), as it never changes).
     
  9. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I stand corrected and I apologize. i just had a look and you are right... apologies.
     
    StarManta likes this.
  10. Winklehner

    Winklehner

    Joined:
    Apr 12, 2013
    Posts:
    20
     
  11. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    No worries, just wanted to make sure :)
     
  12. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Could I ask you for some free tech support, though? :p

    Really curious to learn how to set the RectTransform's width via code.... ;)
    Any clues? I'm about to start a thread to find out cause according to me, it can't be done.

    rect.Set apparently works on a copy of the data so does nothing (as I understand it) and the rect property itself is read only so no way of setting the rect.width
     
  13. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Everything is accessible, but it's a little bit roundabout. The RectTransform UI basically shows you and lets you edit properties that don't directly exist. Everything in script is done via offsetMin and offsetMax, and anchorMin and anchorMax. It's all possible, just less direct. Some convenience API functions to match the boxes available in the UI would be a nice addition in future releases...
     
  14. DESTRUKTORR

    DESTRUKTORR

    Joined:
    Jul 4, 2012
    Posts:
    22
    sizeDelta also plays a role. It basically behaves as the "outer border" from the anchor points. If you've got anchor points set at the midpoint (max and min both set to (0.5, 0.5)) then the sizeDelta behaves sort of like a "width" (sizeDelta.x) and "height" (sizeDelta.y) property.

    It's admittedly a bit confusing, given the serialized version's structure vs. the actual object structure, but as you said, it is all there, and publicly available.
     
  15. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Holy ****monkeys!!! I can't believe that actually worked! OMG! :D

    My ENTIRE project just came to a dead stop because of this issue and now it actually works again... Dude, I give you many, many thanks! :)
     
  16. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I noticed that delta variable but I must plead ignorant to how to use it... Might I bother you for a practical example, please?

    I have a scroll area that is anchored horizontally at 10% from the edge of the canvas but has a fixed 200 unit height. I want to insert 20 objects into the content area of this scroll area, each one occupying 33% of the width of the scroll area. Thus I need to make the content area 20 * (parent.transFormRect.rect.width * 0.33f)

    How would I use the sizeDelta to set the content area this way?
     
    Last edited: Aug 22, 2014
  17. DESTRUKTORR

    DESTRUKTORR

    Joined:
    Jul 4, 2012
    Posts:
    22
    The anchor points more or less form a "rect."

    The sizeDelta makes the edges of the content rect push outward (away from the center of the anchor points) or inward (if one or both coordinates are negative) relative to the corresponding edge on the anchor rect by the number of pixels set on the corresponding axis.

    For example, if you had your anchor points set to (0.1, 0.1) and (0.9, 0.9) (min and max, respectively), setting your sizeDelta to (5,5) will set the edges of the content rect to 5 pixels further in each direction from the anchor rect.

    The left edge would be 5 pixels less than 10% of the width of the parent, the right edge would be 5 pixels further than 90% of the width of the parent, and similarly with the top and bottom edges, now set to 5 pixels below 10% of the height, and 5 pixels above 90% of the height.

    It's a bit clunky at times, but it's actually quite nice if you've got anchor points at the midpoint of the shape (both set to (0.5, 0.5)), as it gives you direct control over the rect's size in a very intuitive way (mind you it's measured in pixels).
     
    MrDude likes this.
  18. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Use the Transform.SetParent method with the worldPositionStays argument set to false. Normally when setting a parent, the position, rotation, and scale of the object is adjusted such that it keeps the same alignment in world space. The new SetTransform method allows keeping the local position, rotation, and scale instead.
     
  19. z37

    z37

    Joined:
    Feb 13, 2014
    Posts:
    29
    Well, I think that the best way is to use layout components. Let your Panel with content have a "Content Size Fitter" component, then make sure each new object that you want to insert as a child of a panel has a "Layout Element" component, then just apply a SetAsLastSibling() method on object. Play with layout components options to reach necessary result.
    This way of doing this is easier, because you don't need to mess up with anything like expanding, scaling and changing position.
     
  20. PhusionDev

    PhusionDev

    Joined:
    Sep 14, 2012
    Posts:
    1
    This saved my life.. thank you!
     
    ergalleg likes this.