Pages

Friday, 13 March 2015

New UI implementation using c# scripts in Unity 3D

Introduction

New UI is introduced from Unity 4.6 on wards. New UI controls will be added in the similar way as other components in Unity.
Basic step is to create a GameObject and then add the required UI elements and other components to it. If you want to have an hierarchy of UI controls then add child GameObject and continue the process mentioned above.

Here we will create a simple dialog as show below just using scripts.



Name spaces for the new UI controls

    1 using UnityEngine.UI;
    2 using UnityEngine.EventSystems;
    3 using UnityEngine.Events;

MonoBehaviour implementation

We will have a game object added in to our scene and add a script to the object. From this script we will create UI controls.

    1 public class UICreator : MonoBehaviour {
    2 
    3     // Layer 5 is used for UI
    4     private const int LayerUI = 5;
    5 
    6     void Start () {
    7         CreateUI();
    8     }
    9 
   10     void Update () {
   11     }
   12 
   13     private void CreateUI() {
   14         // we will have the code to create UI here
   15     }
   16 }
   17 

Canvas Object

All the UI controls in the NEW UI should be a child of Canvas class. Lets start by creating Canvas class.
We will create a GameObject for canvas first and then set the layer to UI and also make it child of current GameObject.
Actual Canvas UI class and other required component will be added to this GameObject.

    1 GameObject canvasObject = new GameObject("Canvas");
    2 canvasObject.layer = LayerUI;
    3 canvasObject.transform.SetParent(parent);
   

Now we will add components RectTransform, Canvas, CanvasScaler and GraphicRaycaster.

    1 RectTransform canvasTrans = canvasObject.AddComponent<RectTransform>();
    2 
    3 Canvas canvas = canvasObject.AddComponent<Canvas>();
    4 canvas.renderMode = RenderMode.ScreenSpaceOverlay;
    5 canvas.pixelPerfect = true;
    6 
    7 CanvasScaler canvasScal = canvasObject.AddComponent<CanvasScaler>();
    8 canvasScal.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
    9 canvasScal.referenceResolution = new Vector2(800, 600);
   10 
   11 canvasObject.AddComponent<GraphicRaycaster>();
   12 

Creating event system

Next we will create EventSystem so that our controls will responds to events.
    1
    2 GameObject esObject = new GameObject("EventSystem");
    3 esObject.transform.SetParent(canvasObject.transform);
     

Just like the Canvas creation we will now add components to the event system GameObject

    1 EventSystem esClass = esObject.AddComponent<EventSystem>();
    2 esClass.sendNavigationEvents = true;
    3 esClass.pixelDragThreshold = 5;
    4 
    5 StandaloneInputModule stdInput = esObject.AddComponent<StandaloneInputModule>();
    6 stdInput.horizontalAxis = "Horizontal";
    7 stdInput.verticalAxis = "Vertical";
    8 
    9 esObject.AddComponent<TouchInputModule>();

Panel to hold UI controls

We will use a Panel to hold our UI controls, so lets create the Panel now.

    1 GameObject panelObject = new GameObject("Panel");
    2 panelObject.transform.SetParent(canvasObject.transform);
    3 panelObject.layer = LayerUI;
    


Now lets add the RectTransform component, RectTransform will be stretched which takes 80% of the Canvas size.

    1 RectTransform trans = panelObject.AddComponent<RectTransform>();
    2 trans.anchorMin = new Vector2(0, 0);
    3 trans.anchorMax = new Vector2(1, 1);
    4 trans.anchoredPosition3D = new Vector3(0, 0, 0);
    5 trans.anchoredPosition = new Vector2(0, 0);
    6 trans.offsetMin = new Vector2(0, 0);
    7 trans.offsetMax = new Vector2(0, 0);
    8 trans.localPosition = new Vector3(0, 0, 0);
    9 trans.sizeDelta = new Vector2(0, 0);
   10 trans.localScale = new Vector3(0.8f, 0.8f, 1.0f);
   11 
   12 panelObject.AddComponent<CanvasRenderer>();
   

Now we will add a background image to the panel. We will use a simple png file as the background. png file should be present at Assets\Resources.

    1 Image image = panelObject.AddComponent<Image>();
    2 Texture2D tex = Resources.Load<Texture2D>("panel_bkg");
    3 image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));

Creating Text UI control

Now lets go and create Text control.

    1 GameObject textObject = new GameObject("Text");
    2 textObject.transform.SetParent(panelObject.transform);
    3 textObject.layer = LayerUI;
  

Now we will add RectTransform which sets the size and position, CanvasRenderer and the actual Text component. Text will be positioned at (x, y)

    1 RectTransform trans = textObject.AddComponent<RectTransform>();
    2 trans.sizeDelta.Set(w, h);
    3 trans.anchoredPosition3D = new Vector3(0, 0, 0);
    4 trans.anchoredPosition = new Vector2(x, y);
    5 trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
    6 trans.localPosition.Set(0, 0, 0);
    7 
    8 textObject.AddComponent<CanvasRenderer>();
    9 
   10 Text text = textObject.AddComponent<Text>();
   11 text.supportRichText = true;
   12 text.text = message;
   13 text.fontSize = fontSize;
   14 text.font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;
   15 text.alignment = TextAnchor.MiddleCenter;
   16 text.horizontalOverflow = HorizontalWrapMode.Overflow;
   17 text.color = new Color(0, 0, 1);

Creating buttons

Creating button has two part, first creation of the button itself and the second part is creating a text object for displaying the text.
    1 GameObject buttonObject = new GameObject("Button");
    2 buttonObject.transform.SetParent(panelObject.transform);
    3 buttonObject.layer = LayerUI;


Now lets create the RectTransform and position our button accordingly.

    1 RectTransform trans = buttonObject.AddComponent<RectTransform>();
    2 SetSize(trans, new Vector2(w, h));
    3 trans.anchoredPosition3D = new Vector3(0, 0, 0);
    4 trans.anchoredPosition = new Vector2(x, y);
    5 trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
    6 trans.localPosition.Set(0, 0, 0);
    7 
    8 buttonObject.AddComponent<CanvasRenderer>();

Setting background image for button will be similar to that of panel's. The png file button_bkg.png should be at Assets\Resources as before.

    1 Image image = buttonObject.AddComponent<Image>();
    2 Texture2D tex = Resources.Load<Texture2D>("button_bkg");
    3 image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));

Creating the actual button, eventListner is of type UnityAction.

    1 Button button = buttonObject.AddComponent<Button>();
    2 button.interactable = true;
    3 button.onClick.AddListener(eventListner);

Next we will have to create a text object with the message and add it to the buttonObject. We can use the same code which creates the text object for this. So I am not a showing that here.

Ok now we have created NEW UI using just scripts.

Complete source code is pasted below. Please note that the code is no where near release quality.

using UnityEngine;
using System.Collections;
using System.IO;

using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public class UICreator : MonoBehaviour {

    private const int LayerUI = 5;

    void Start () {
        CreateUI();
    }
    
    void Update () {
    }


    private void CreateUI() {
        
        GameObject canvas = CreateCanvas(this.transform);

        CreateEventSystem(canvas.transform);

        GameObject panel = CreatePanel(canvas.transform);

        CreateText(panel.transform, 0, 100, 160, 50, "Message", 32);
        CreateText(panel.transform, 0, 0, 160, 50, "Are you sure, you want to exit?", 24);

        CreateButton(panel.transform, -100, -100, 160, 50, "Yes", delegate {OnExit();});
        CreateButton(panel.transform, 100, -100, 160, 50, "No", delegate {OnCancel();});
    }

    private GameObject CreateCanvas(Transform parent) {
        // create the canvas
        GameObject canvasObject = new GameObject("Canvas");
        canvasObject.layer = LayerUI;

        RectTransform canvasTrans = canvasObject.AddComponent<RectTransform>();
        
        Canvas canvas = canvasObject.AddComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        canvas.pixelPerfect = true;

        CanvasScaler canvasScal = canvasObject.AddComponent<CanvasScaler>();
        canvasScal.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        canvasScal.referenceResolution = new Vector2(800, 600);
        
        GraphicRaycaster canvasRayc = canvasObject.AddComponent<GraphicRaycaster>();

        canvasObject.transform.SetParent(parent);
        
        return canvasObject;
    }

    private GameObject CreateEventSystem(Transform parent) {
        GameObject esObject = new GameObject("EventSystem");

        EventSystem esClass = esObject.AddComponent<EventSystem>();
        esClass.sendNavigationEvents = true;
        esClass.pixelDragThreshold = 5;

        StandaloneInputModule stdInput = esObject.AddComponent<StandaloneInputModule>();
        stdInput.horizontalAxis = "Horizontal";
        stdInput.verticalAxis = "Vertical";

        TouchInputModule touchInput = esObject.AddComponent<TouchInputModule>();

        esObject.transform.SetParent(parent);

        return esObject;
    }

    private GameObject CreatePanel(Transform parent) {
        GameObject panelObject = new GameObject("Panel");
        panelObject.transform.SetParent(parent);

        panelObject.layer = LayerUI;

        RectTransform trans = panelObject.AddComponent<RectTransform>();
        trans.anchorMin = new Vector2(0, 0);
        trans.anchorMax = new Vector2(1, 1);
        trans.anchoredPosition3D = new Vector3(0, 0, 0);
        trans.anchoredPosition = new Vector2(0, 0);
        trans.offsetMin = new Vector2(0, 0);
        trans.offsetMax = new Vector2(0, 0);
        trans.localPosition = new Vector3(0, 0, 0);
        trans.sizeDelta = new Vector2(0, 0);
        trans.localScale = new Vector3(0.8f, 0.8f, 1.0f);

        CanvasRenderer renderer = panelObject.AddComponent<CanvasRenderer>();
        
        Image image = panelObject.AddComponent<Image>();
        Texture2D tex = Resources.Load<Texture2D>("panel_bkg");
        image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height),
                                                  new Vector2(0.5f, 0.5f));

        return panelObject;
    }
    
    private GameObject CreateText(Transform parent, float x, float y,
                                      float w, float h, string message, int fontSize) {
        GameObject textObject = new GameObject("Text");
        textObject.transform.SetParent(parent);

        textObject.layer = LayerUI;

        RectTransform trans = textObject.AddComponent<RectTransform>();
        trans.sizeDelta.Set(w, h);
        trans.anchoredPosition3D = new Vector3(0, 0, 0);
        trans.anchoredPosition = new Vector2(x, y);
        trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
        trans.localPosition.Set(0, 0, 0);

        CanvasRenderer renderer = textObject.AddComponent<CanvasRenderer>();
        
        Text text = textObject.AddComponent<Text>();
        text.supportRichText = true;
        text.text = message;
        text.fontSize = fontSize;
        text.font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;
        text.alignment = TextAnchor.MiddleCenter;
        text.horizontalOverflow = HorizontalWrapMode.Overflow;
        text.color = new Color(0, 0, 1);

        return textObject;
    }
    
    private GameObject CreateButton(Transform parent, float x, float y,
                                        float w, float h, string message,
                                        UnityAction eventListner) {
        GameObject buttonObject = new GameObject("Button");
        buttonObject.transform.SetParent(parent);

        buttonObject.layer = LayerUI;

        RectTransform trans = buttonObject.AddComponent<RectTransform>();
        SetSize(trans, new Vector2(w, h));
        trans.anchoredPosition3D = new Vector3(0, 0, 0);
        trans.anchoredPosition = new Vector2(x, y);
        trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
        trans.localPosition.Set(0, 0, 0);

        CanvasRenderer renderer = buttonObject.AddComponent<CanvasRenderer>();

        Image image = buttonObject.AddComponent<Image>();

        Texture2D tex = Resources.Load<Texture2D>("button_bkg");
        image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height),
                                                  new Vector2(0.5f, 0.5f));

        Button button = buttonObject.AddComponent<Button>();
        button.interactable = true;
        button.onClick.AddListener(eventListner);

        GameObject textObject = CreateText(buttonObject.transform, 0, 0, 0, 0,
                                                   message, 24);

        return buttonObject;
    }

    private static void SetSize(RectTransform trans, Vector2 size) {
        Vector2 currSize = trans.rect.size;
        Vector2 sizeDiff = size - currSize;
        trans.offsetMin = trans.offsetMin -
                                  new Vector2(sizeDiff.x * trans.pivot.x,
                                      sizeDiff.y * trans.pivot.y);
        trans.offsetMax = trans.offsetMax + 
                                  new Vector2(sizeDiff.x * (1.0f - trans.pivot.x),
                                      sizeDiff.y * (1.0f - trans.pivot.y));
    }

    private void OnExit()
    {
        Application.Quit();
    }

    private void OnCancel()
    {
        GameObject.Destroy(this);
    }
}
 

UI Using prefab

Another, may be better, option is to use prefab. Create the UI using the unity editor and create prefab.


Using prefab we can launch the UI as shown below, Here DialogCanvas is the prefab and Yes and No are name of the GameObjects which has Button component.
    1 GameObject termsAndConditionCanvas = (GameObject)Instantiate(Resources.Load("DialogCanvas"));
    2 termsAndConditionCanvas.transform.SetParent(this.transform);
    3 GameObject decline = (GameObject)GameObject.Find("Yes");
    4 Button declineButton = decline.GetComponent<Button>();
    5 declineButton.onClick.AddListener(delegate{OnYesClicked();});
    6 GameObject accept = (GameObject)GameObject.Find("No");
    7 Button acceptButton = (Button)accept.GetComponent<Button>();
    8 acceptButton.onClick.AddListener(delegate{OnNoClicked();});

OnYesClicked and OnNoClicked is declared as,
    1 void OnYesClicked()
    2 {}
    3 void OnNoClicked()
    4 {}

3 comments:

  1. Hi,

    Nice tutorial. Can we add UI elements to script through inspector panel?

    Thanks.

    ReplyDelete
    Replies
    1. yes, see https://unity3d.com/learn/tutorials/modules/intermediate/editor/adding-buttons-to-inspector

      Delete
  2. Exactly what I looking for! Thank you very much for the tutorial!

    ReplyDelete