Author Topic: UIGrid with one element : avoid centering  (Read 6342 times)

RDeluxe

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 18
    • View Profile
UIGrid with one element : avoid centering
« on: June 16, 2014, 08:45:31 AM »
I'm adding prefabs to my Grid (by code). The data are coming from the server. Problem is, when I only have 1 element in my grid, this is happening :



I'm trying to add an invisible element to my grid to solve this, but I'm not sure it's the best way to do.

With several elements it's working perfectly :


ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #1 on: June 16, 2014, 09:06:53 AM »
So what is happening, exactly?

Your UI is using odd values. Select the UIRoot and hit ALT+SHIFT+P.

You need to use whole integer values everywhere, not floating point ones. You will never have a crisp looking UI if you use floating point values.

RDeluxe

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 18
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #2 on: June 16, 2014, 03:19:05 PM »
Well, the floating point values are coming from calculation (automatic resizing in code). They are so weird because I was in "free mode", but you're right I need to check that.

Well, as I explained, my problem here is that I don't want my grid to look like this when i only have one element (the only element being centered). When there is only one element the grid contains only one column, and does not consider the second one (which is empty). I find this behavior pretty weird.

I was wondering if the only solution in this case was to populate the grid with an invisible element ...


ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #3 on: June 17, 2014, 12:54:22 PM »
Change the grid's pivot point to top left and it won't be centered.

RDeluxe

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 18
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #4 on: June 17, 2014, 02:28:26 PM »
No change :

With a Top Left GridPivot :


With a Top Left GridPivot and Top Left Scrollview content origin, same thing.

Actually, with only one element, the GridPivot is absolutly useless (I tried all the Pivot). The problem seems linked to the grid size.

Frankly, I'm amazed to realize how hard it is to implement a simple scrollable grid, populated on the fly. Here is my exact setup (I can share the project with you by private message if you want, it may be easier this way, but I would like to avoid this as much as possible);

My hierachy :



PlaceList :



Grid :



The script attached to this "Screen" (root UIPanel). This script takes a JSON containing a list of places, and instantiate a prefab for each place (filling some TextField).
It also manages the resizing of the Grid cell width and height, and the grid elements, in case of resolution change, as I want my grid cell to take a certain amount of space of any resolution (i.e my cell width is the screen width divided by 2.2).

  1. using UnityEngine;
  2. using System.Collections;
  3. using Newtonsoft.Json;
  4. using System.Collections.Generic;
  5.  
  6. public class PlacesScreen : MonoBehaviour
  7. {
  8.     // Used to check if the screen resolution changed
  9.     // TODO : Find a better solution than this one
  10.     public float ScreenStartWidth;
  11.     public float ScreenStartHeight;
  12.  
  13.     List<Place> places;
  14.     UIScrollView PlacesList;
  15.     UIGrid PlacesGrid;
  16.     GameObject FileList;
  17.  
  18.     // Hardcoded for testing purposes only
  19.     string json = @"[]";
  20.  
  21.     // Use this for initialization
  22.     void Start()
  23.     {
  24.         places = JsonConvert.DeserializeObject<List<Place>>(json);
  25.         PlacesList = gameObject.GetComponentInChildren<UIScrollView>() as UIScrollView;
  26.         PlacesGrid = gameObject.GetComponentInChildren<UIGrid>() as UIGrid;
  27.         FileList = GameObject.Find("Grid");
  28.  
  29.         ScreenStartHeight = PlacesList.gameObject.GetComponent<UIPanel>().height;
  30.         ScreenStartWidth = PlacesList.gameObject.GetComponent<UIPanel>().width;
  31.  
  32.         ResizeGrid();
  33.  
  34.         if (PlacesGrid != null && FileList != null && PlacesList != null)
  35.         {
  36.             BetterList<Transform> childList = PlacesGrid.GetChildList();
  37.  
  38.             // Addind the places from the json to the graphic list
  39.             foreach (Place place in places)
  40.             {
  41.                 Debug.Log("Adding a place");
  42.                 GameObject placeFile = Resources.Load("prefabs/File") as GameObject;
  43.                 placeFile.GetComponent<UISprite>().width = (int)PlacesGrid.cellWidth;
  44.                 placeFile.transform.Find("ContentPanel").gameObject.transform.Find("PlaceName").gameObject.GetComponent<UILabel>().text = place.Name;
  45.                 placeFile.transform.Find("ContentPanel").gameObject.transform.Find("CityName").gameObject.GetComponent<UILabel>().text = place.City;
  46.                 placeFile.transform.Find("ContentPanel").gameObject.transform.Find("TypeName").gameObject.GetComponent<UILabel>().text = place.Type;
  47.                 NGUITools.AddChild(FileList, placeFile);
  48.                 childList.Add(placeFile.transform);
  49.             }
  50.  
  51.             // Small hack to get a nicely presented Grid even with only 1 element
  52.             // TODO : Redo this ...
  53.             if (childList.size == 1)
  54.             {
  55.             }
  56.         }
  57.     }
  58.  
  59.     // Update is called once per frame
  60.     void Update()
  61.     {
  62.         if (ScreenStartHeight != PlacesList.gameObject.GetComponent<UIPanel>().height || ScreenStartWidth != PlacesList.gameObject.GetComponent<UIPanel>().width)
  63.         {
  64.             // PlacesLisit UIPanel size is changing when the List is dragged, but in this case we don't want to call a Resize()
  65.             if(!PlacesList.isDragging)
  66.             {
  67.                 ResizeGrid();
  68.                 ScreenStartHeight = PlacesList.gameObject.GetComponent<UIPanel>().height;
  69.                 ScreenStartWidth = PlacesList.gameObject.GetComponent<UIPanel>().width;
  70.             }
  71.         }
  72.     }
  73.  
  74.     /// <summary>
  75.     /// Resize the grid considering the screen width
  76.     /// </summary>
  77.     void ResizeGrid()
  78.     {
  79.         Debug.Log("ResizeGrid Called");
  80.         // Resizing the Grid cells' size.
  81.         PlacesGrid.cellWidth = (float)(PlacesList.gameObject.GetComponent<UIPanel>().width / 2.2);
  82.         PlacesGrid.cellHeight = (float)(PlacesGrid.cellWidth / 1.244);
  83.         foreach (Transform place in PlacesGrid.GetChildList())
  84.         {
  85.             place.gameObject.GetComponent<UISprite>().width = (int)PlacesGrid.cellWidth;
  86.         }
  87.         PlacesGrid.Reposition();
  88.         PlacesList.ResetPosition();
  89.     }
  90. }
  91.  

As I said here I have another problem. The ResetPosition() function of the Scrollview is not working if called right after the grid resizing. If I "Reset clipping position" in the editor while in play mode, it's working perfectly (and putting the ResetPosition() in the Update() is working too. I'm not the only one having this problem).

Any help is appreciated, because I really start to get crazy : those are minor bugs, but still.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #5 on: June 18, 2014, 03:34:49 PM »
Grid simply repositions all children, and the "pivot" setting controls which way the grid will expand. Top-left pivot means the grid will expand toward the right and bottom. "Top" pivot you had earlier means the grid will expand horizontally, half the content width to the left and half the content width to the right. Make sure you have "Pivot" tool selected in the top-left, not "Center", and select the grid object. You will notice that the transform gizmo. If this isn't working as you'd like, just do your own positioning. it's as simple as setting transform.localPosition of every game object you dynamically instantiate. I'm not sure why this is giving you so much trouble.

Based on the number of pink outlines, you seem to have a boatload of panels in there... any particular reason for that? Background panel + scroll view panel should be more than enough.

Looking at your code, you seem to be modifying the prefab... this is just plain wrong. You need to modify an instance of the prefab, never the prefab itself. Resources.Load gives you a prefab. NGUITools.AddChild(parent, prefab) gives you an instance of this prefab. Only then should you be modifying values.

I don't see any code that calls Reposition() on the grid in your Start() function. You should be doing it after instantiating everything -- although might be best to set a flag that you would check in the Update() function instead. Your Update function seems to be checking sizes instead for some reason... not sure why. Your reposition function is odd as well. I'm not sure what those magic numbers are that you divide width and height by, but they surely explain the floating point values I see in inspector. As I mentioned, you will never have a crisp looking UI with floating point values.

RDeluxe

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 18
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #6 on: June 18, 2014, 04:06:13 PM »
Well, first, thanks a lot for this detailled answer !

I for sure have some trouble with some basics features of Unity, like the instantiation of the prefab for example. I checked almost all the tutorial on their website but, heh, you gotta make some mistakes at one point. Thanks for this tip.

My Update() function is checking the screen size to resize my grid in case of a resolution change, because I realized that changing the screen sized at runtime did not resize my grid like I want (based on the screen width). It's a pretty basic thing, the "magic numbers" are simply the proportion I want my UI to have (as soon as you start to work with graphic artists and 2D drawing for the UI, you will never have plain simple values). For example, I want my grid cells to have a widht equals to the screen width divided by 2.2. I just need to round up the results to avoid floating point values.

The UI is pretty dynamic, and I prefer to separate all the dynamic parts into different UIPanel : The screen, the ressource bar on the top right (with dynamic values), the scrollview, and each "file" (my prefab).
Maybe it's not the right thing to do, but it seemed more natural to use the UIPanel as a container. Plus, I saw you writing somewhere that for dynamic elements UIPanel was useful : it would reload/refresh only its content, and not redraw the whole screen (as far as I understood it).

What I don't really understand is the Grid pivot thing. as you said, the Top option should put half the content on the left, and half on the right. Combined with a "Top" content origin on my Scrollview, I don't really undestand why I don't have a perfectly centered grid automatically. I guess I'll have to reposition everything by my self then, but it seemed pretty overkill for something like this.

I'm still a bit troubled about the 1 element case. I understand that the grid is only positionning stuff, so in that case I just need to reposition the element by myself but, well, if in the end I need to reposition so many things by hand, I'm going to end up doing the same thing I was doing with GameClosure : everything by hand. I guess I was expecting a bit too much from Unity in term of simplicity (WYSIWYG and all that stuff).

Thanks a lot for your answer, it really helps Unity beginners like me.

Edit : Don't take me wrong, I think that NGUI is a wonderful tool, especially compared to what is done about GUI in Unity for the moment. But I was not expecting small problems like those one to slow me down so much : my goal was to re-implement the UI of the game as fast as possible, and then focus on more important things. And those small problems, when you are not used to Unity nor NGUI, can take a tremendous amount of time, just to figure out what's going wrong (i.e, the ResetPosition() of my scrollview not working correctly).
« Last Edit: June 18, 2014, 04:45:10 PM by RDeluxe »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #7 on: June 18, 2014, 05:49:46 PM »
Note that the centering actually depends on the scroll direction though. For example, if you're scrolling vertically and choose "Top Left" as the content origin, it's not going to move the content horizontally. Only vertically.

Horizontal position will remain in the same place, and you should be aware that the scroll view's position may not lie in the center of the scroll view. Switch to any tool other than a Move tool with the scroll view selected to see where it actually lies.

I just did a quick test on my end... Created a new scene, added a scroll view, placed a UIGrid underneath it and wrote a script to instantiate items under it. After instantiation I call Reposition() on the grid, and items show up in their expected position. If I add a scroll bar, then the scroll bar's value is used by the scroll view right away -- and with the provided controls that come with NGUI this means the scroll view is scrolled down beyond the halfway point. When I adjust the scroll bar's starting value to 0, it starts on top as expected. I even don't call ResetPosition() or anything else on the scroll view.

The only thing I see that may cause issues is that the scroll bar's size is not adjusted properly automatically. I've added some code to the UIPanel itself to do that that you will find in the next update.
  1. public class Test : MonoBehaviour
  2. {
  3.         public GameObject prefab;
  4.  
  5.         void Start ()
  6.         {
  7.                 for (int i = 0; i < 10; ++i)
  8.                 {
  9.                         NGUITools.AddChild(gameObject, prefab);
  10.                 }
  11.                 GetComponent<UIGrid>().Reposition();
  12.         }
  13. }

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #8 on: June 18, 2014, 05:52:46 PM »
I just realized something... I'm guessing your confusion lies in that you expect the content to show up in the top-left corner of the scroll view... but this will never happen because your scroll view is restricted to vertical movement. NGUI only adjusts the axis that it allows movement on.

RDeluxe

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 18
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #9 on: June 19, 2014, 04:31:40 AM »
Note that the centering actually depends on the scroll direction though. For example, if you're scrolling vertically and choose "Top Left" as the content origin, it's not going to move the content horizontally. Only vertically.

Horizontal position will remain in the same place, and you should be aware that the scroll view's position may not lie in the center of the scroll view. Switch to any tool other than a Move tool with the scroll view selected to see where it actually lies.

I just did a quick test on my end... Created a new scene, added a scroll view, placed a UIGrid underneath it and wrote a script to instantiate items under it. After instantiation I call Reposition() on the grid, and items show up in their expected position. If I add a scroll bar, then the scroll bar's value is used by the scroll view right away -- and with the provided controls that come with NGUI this means the scroll view is scrolled down beyond the halfway point. When I adjust the scroll bar's starting value to 0, it starts on top as expected. I even don't call ResetPosition() or anything else on the scroll view.

The only thing I see that may cause issues is that the scroll bar's size is not adjusted properly automatically. I've added some code to the UIPanel itself to do that that you will find in the next update.
  1. public class Test : MonoBehaviour
  2. {
  3.         public GameObject prefab;
  4.  
  5.         void Start ()
  6.         {
  7.                 for (int i = 0; i < 10; ++i)
  8.                 {
  9.                         NGUITools.AddChild(gameObject, prefab);
  10.                 }
  11.                 GetComponent<UIGrid>().Reposition();
  12.         }
  13. }

Interesting. I don't want a scrollbar, but I was wondering if I could do that (programmatically scroll my scrollview to the top).

I just realized something... I'm guessing your confusion lies in that you expect the content to show up in the top-left corner of the scroll view... but this will never happen because your scroll view is restricted to vertical movement. NGUI only adjusts the axis that it allows movement on.

No actually this behavior is what I want. My only problem is when my grid only have 1 element : the 2nd column is not created and not considered by the grid nor the scrollview, and so, logically, my only element is centered.



I guess the workaround is to reposition manually when I only have 1 element.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: UIGrid with one element : avoid centering
« Reply #10 on: June 19, 2014, 06:12:56 PM »
Ah, yes. The second column won't be created for you if there is only one element.