Sunday, February 8, 2015

Dynamic Grid Layout in Unity3d 4.6 UI-Part2

Standard
Hello All:

This post is part 2 of this post. In this post I will share code to populate grid layout with the data which is downloaded using a web service.


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

If you have not read the first post, go check it out I will be here as this post heavily depends on part 1.

So let us start..

We will start by duplicating the scene which we created in the first part.

Now duplicate p_Item prefab and rename it p_Item2. Add an image to p_Item2 to show the image which we will download from net.

Grid Layout


We will access a web service to download json data and parse it to display the list.
I have explained how to implement a Request Manager to make web calls.

We will use Json.Fx to parse json data. You can get a working Json.Fx dll from here.
 
We will update RequestManager class to suit our need.


using UnityEngine;
using System.Collections;

using System.Collections.Generic;
using System.Runtime.InteropServices;
using System;
using System.Collections.Specialized;


public class RequestManager : MonoBehaviour
{
    private const int REQUEST_MAX_POST_TIME = 30;
    private Queue<Request> requestQueue = new Queue<Request>();
    private bool queueIsProcessing = false;
    public static RequestManager Instance;

    public void Awake()
    {
        if (Instance != null)
        {
            Destroy(this.gameObject);
            return;
        }
        DontDestroyOnLoad(this.gameObject);
        Instance = this;
        requestQueue = new Queue<Request>();
    }

    public void AddRequest(Request request)
    {
        PostRequest(request);
    }

    private void PostRequest(Request request)
    {
        request.RequestTme = Time.time;
        requestQueue.Enqueue(request);
        try
        {
            StartCoroutine(ProcessQueue());
        }
        catch (Exception ex)
        {
            Debug.LogException(ex);
        }
    }

    private IEnumerator ProcessQueue()
    {
        if (queueIsProcessing)
            yield break;

        queueIsProcessing = true;
        while (requestQueue.Count > 0)
        {
            Request request = requestQueue.Peek();
            float postTime = Time.time;
            WWW www = new WWW(request.URL);
            while (!www.isDone)
            {
                if (Time.time - postTime > REQUEST_MAX_POST_TIME)
                {
                    break;
                }
                yield return null;
            }
            if (request.OnComplete != null)
            {
                request.OnComplete(www, request.CurrentGameObject);
            }
            requestQueue.Dequeue();
        }
        queueIsProcessing = false;
    }
}

public class Request
{
    public float RequestTme
    {
        get;
        set;
    }

    public string Data
    {
        get;
        set;
    }

    public String URL
    {
        get;
        set;
    }

    public GameObject CurrentGameObject { get; set; }
    public Action<WWW, GameObject> OnComplete
    {
        get;
        set;
    }

}
Now we will update the Two script component which we created in part1.
  1. One for list generation
 Create a C# script: DynamicScrollableGridFromWeb 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 DynamicScrollableGridFromWeb : 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()
    {
        currentCount.text = "Current Count: " + 0.ToString();
        ///Clearing old list
        items.ForEach((go) => GameObject.Destroy(go));
        items.Clear();

        RequestManager.Instance.AddRequest(new Request()
        {
            URL = "http://www.ashwanik.in/blog/getdynamicgriddata",
            OnComplete = (www, go) =>
            {
                if (string.IsNullOrEmpty(www.error))
                {
                    Debug.Log(www.text);
                    DynamicGridData[] data = Pathfinding.Serialization.JsonFx.JsonReader.Deserialize<DynamicGridData[]>(www.text);
                    if (data != null)
                    {
                        for (int index = 0; index < data.Length; index++)
                        {
                            GameObject localItem = (GameObject)Instantiate(item, Vector3.zero, Quaternion.identity);
                            ///Setting parent for the item
                            localItem.transform.SetParent(grid.transform);
                            localItem.GetComponent<Item>().Id = data[index].Id;
                            RequestManager.Instance.AddRequest(new Request()
                              {
                                  URL = data[index].IconPath,
                                  CurrentGameObject = localItem,
                                  OnComplete = (imageWWW, imageGo) =>
                                  {
                                      if (imageGo != null)
                                      {
                                          Sprite sprite = new Sprite();
                                          sprite = Sprite.Create(imageWWW.texture, new Rect(0, 0, 36, 36), Vector2.zero);
                                          imageGo.GetComponent<Item>().currentImage.sprite = sprite;
                                          imageGo.GetComponent<Item>().currentImage.gameObject.SetActive(true);
                                      }
                                  }
                              });
                            ///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);
                        }
                        StartCoroutine(InducedDelay());
                        currentCount.text = "Current Count: " + data.Length.ToString();
                    }
                }
                else
                {
                    Debug.Log(www.error);
                }
                //Sprite sprite = new Sprite();
                //sprite = Sprite.Create(www.texture, new Rect(0, 0, 72, 72), Vector2.zero);
                //action.promoImage.sprite = sprite;
                //action.promoImage.gameObject.SetActive(true);
            }
        });
        yield return null;
        //2. Moving the scroll bar to left
        //scrollBar.value = 0;
    }

    IEnumerator InducedDelay()
    {
        yield return new WaitForSeconds(.1f);
        scrollRect.horizontalNormalizedPosition = 0;
    }
}

public class DynamicGridData
{
    public int Id { get; set; }
    public string IconPath { get; set; }
}


Now Attach DynamicScrollableGridFromWeb to the Canvas and assign the public variables




Create an empty game object and attach RequestManager script to it.
We need to make sure that RequestManager should be the first script to get called when our scene is loaded. To make this sure,

Click Edit->Project Settings->Script Execution Order



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.

0 comments :

Post a Comment