Monday, February 2, 2015

Dynamic Grid Layout in Unity3d 4.6 UI

Standard
Hello All:

Often in our games we need scrollable lists to show scores, inventory items, in-app purchase UI etc. In this post I will show how to make a dynamic scrollable list to achieve earlier mentioned UI screens.

This post will be a part of Two post series.
  1. Part 1 will show list generation using static data.
  2. Part 2 will show list generation using data fetched from a web service.

By the end of tutorial, we will have our camera doing something similar to this.

This post will require understanding of following components
  1. Mask: This component is used to mask the child game objects. More details can be found here.
  2. ScrollRect: This component is used to provide horizontal and vertical scrolling. More details can be found here.
So let us start..

We will start by creating an empty screen.
 
Then create a panel in the scene.

Dynamic grid in Unity3d 4.6UI
Panel

Then Add "Mask" component to the panel (By clicking Add Component and searching for Mask).
Add ScrollRect with Horizontal checked and vertical unchecked (We are create a horizontal list).



Dynamic grid in Unity3d 4.6UI



Now add an empty GameObject to the panel and name it "Grid", this will serve as container for all the items of the grid. Assign this Grid to "Content"of ScrollRect component of the panel.

Dynamic grid in Unity3d 4.6UI
Grid with GridLayoutGroup and Content Size filter

As we are creating a horizontal list so we will constraint on Row Count.

Now Add an Image to Grid, this will be template for the items which we will add on run time.

Dynamic grid in Unity3d 4.6UI

Now create a prefab out of the image we just added and rename it p_Item.

We can keep the original Item on the scene and disable it. In case in future we want to change our prefab we can use this for visualization.

Now we will be adding Two script component.
  1. One for list generation
  2. Second for the item to handle item specific actions
 Create a C# script: DynamicScrollableGrid and add following code. I have added comments to explain the code.

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;

public class DynamicScrollableGrid : MonoBehaviour
{
    /// <summary>
    /// Scroll bar for the list
    /// </summary>
    public Scrollbar scrollBar;
    /// <summary>
    /// Item prefab
    /// </summary>
    public GameObject item;
    List<GameObject> items;
    /// <summary>
    /// Grid which contains the items
    /// </summary>
    public GridLayoutGroup grid;
    public ScrollRect scrollRect;
    public Text currentCount;
    // Use this for initialization
    Vector3 autoLocalScale;

    void Start()
    {
        items = new List<GameObject>();
        autoLocalScale = new Vector3(1, 1, 1);
        RefreshList();
    }

    public void RefreshList()
    {
        StartCoroutine(GenerateItems());
    }

    public IEnumerator GenerateItems()
    {
        System.Random rand = new System.Random();
        currentCount.text = "Current Count: " + 0.ToString();
        ///Clearing old list
        items.ForEach((go) => GameObject.Destroy(go));
        items.Clear();

        int randomItemCount = rand.Next(20, 100);
        for (int index = 0; index < randomItemCount; index++)
        {
            GameObject localItem = (GameObject)Instantiate(item, Vector3.zero, Quaternion.identity);
            ///Setting parent for the item
            localItem.transform.SetParent(grid.transform);
            localItem.GetComponent<Item>().Id = index + 1;

            ///Important as instatiated item can have random scale and might not be visible
            localItem.transform.localScale = autoLocalScale;
            localItem.transform.localPosition = Vector3.zero;
            items.Add(localItem);
        }
        yield return new WaitForSeconds(.1f);
        ///1. Two ways to move the scroll to first item
        scrollRect.horizontalNormalizedPosition = 0;
        currentCount.text = "Current Count: " + randomItemCount.ToString();

        //2. Moving the scroll bar to left
        //scrollBar.value = 0;
    }
}

Create another C# script: Item and add following code.

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class Item : MonoBehaviour
{

    public int Id;
    public void Start()
    {
        foreach (Transform localTransform in transform)
        {
            if (localTransform.name == "Id")
            {
                localTransform.GetComponent<Text>().text = (Id).ToString();
            }
        }
    }

    public void OnClick()
    {
        GameObject.Find("ButtonClicked").GetComponent<Text>().text = "Item #" + Id.ToString() + " Clicked";
        Debug.Log("Item #" + Id.ToString() + " Clicked");
    }
}

Now Attach DynamicScrollableGrid to the Canvas and assign the public variables

Dynamic grid in Unity3d 4.6UI


We are passing scrollbar to the script as we can set the starting position of the list by changing the scrollbar value.

Add Item to p_Item and add event handler for onClick

Dynamic grid in Unity3d 4.6UI


Now save the scene and run it. You will see a list as shown below


 
Hope this helps. The full working code can be found here.

Thanks for printing this post. Hope you liked it.
Keep visiting and sharing.
Thanks,
Ashwani.

1 comment :

  1. Could be clearer if the scrollBar and the Refresh button addittion shown .

    ReplyDelete