How to Unity3D – Why Use UI instead of GUI / IMGUI

unity-logoIn this use-case, you will see the difference between GUI and UI API. And why use the later in your game, for constancy between platforms and API long term support.

screenshot0055

A lot of tutorials and feedbacks on Internet show code using GUI API. But it not a good approach to Unity3D.

Here si the official Unity3D documentation: “The IMGUI system is not generally intended to be used for normal in-game user interfaces … For that you should use Unity’s main GameObject-based UI system“.

https://docs.unity3d.com/Manual/GUIScriptingGuide.html

Steps

  • GUI Example
  • UI Example
    • Pros / Cons
  • Project Code
  • Demos

GUI Example

The GUI is the fastest way to build UI, like on other platforms like webpage (HTML/JS/CSS), or Android (Java). But it’s not intended to be use in game code. It’s just for testing en Unity Editor.

ScreenShot009.png

https://docs.unity3d.com/Manual/GUIScriptingGuide.html

  • Pros
    • Productive (simple)
    • Maintainable (less code, params)
  • Cons
    • GUI is deprecated since Unity3D 5.x!!!
    • Limited (don’t work properly on other plateforme than Windows)
    • coordinates start at top/left (like webpage/android, are absolute in pixel)

UI Example

Unity3d.UI is the actual UI API. It’s complex, but fully customizable. But there is no convention or default behavior (like size, colors, textures).

ScreenShot010.png

https://docs.unity3d.com/Manual/HOWTO-UIMultiResolution.html

  • Pros
    • relative position (to parent object)
    • consistancy, responsive (same UI between plateformes)
  • Cons
    • Need an Event system (EventManager)
    • very verbose (like WPF, GWT, or SWING)
    • complex (you need lot of Canvas, GameObjects)
    • coordinates start at bot/left

Project Code

You can download or code this example project.

  • create empty 2D project
  • create a Startup.cs script for the Unity3D editor to add Script to scene automatically.
  • create three examples
  • run it on windows
  • run it on mobile
  • compare the result

https://github.com/DamienFremont/blog/tree/master/20170602-unity3d-use-ui-instead-of-gui-or-imgui/

Assets/Startup.cs

using UnityEngine;
using UnityEditor;

// http://docs.unity3d.com/Manual/RunningEditorCodeOnLaunch.html
[InitializeOnLoad]
public class Startup
{
	static Startup ()
	{
		GameObject gameObject;
		gameObject = GameObject.Find("Main Camera");

		Component script1 = gameObject.GetComponent<GUIExample>();
		if (script1 == null) {
			gameObject.AddComponent<GUIExample> ();
		}

		Component script2 = gameObject.GetComponent<UIExample>();
		if (script2 == null) {
			gameObject.AddComponent<UIExample> ();
		}
	}
}

https://docs.unity3d.com/ScriptReference/GameObject.Find.html

ScreenShot003.png

ScreenShot012.png

Assets/Scripts/GUIExample.cs

using System;
using UnityEngine;

// https://docs.unity3d.com/Manual/GUIScriptingGuide.html
public class GUIExample : MonoBehaviour
{
	// Use this for initialization
	void OnGUI ()
	{
		Rect rect = new Rect (10, 10, 100, 100);
		Texture2D tex = new Texture2D (1, 1);
		tex.SetPixels(new Color[]{Color.grey});

		GUIStyle style = new GUIStyle ();
		style.normal.background = tex;
		// https://docs.unity3d.com/ScriptReference/GUILayout.BeginArea.html
		GUILayout.BeginArea (rect, tex, style);
		if (GUILayout.Button ("Click me"))
			action ();
		if (GUILayout.Button ("Or me"))
			action ();
		GUILayout.EndArea ();
	}

	private void action ()
	{
		Debug.Log ("Hello!");
	}
}

Assets/Scripts/UIExample.cs

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

public class UIExample : MonoBehaviour
{
	// Use this for initialization
	void Start ()
	{
		Rect rect = new Rect (0, 0, 100, 100);
		Texture2D tex = new Texture2D (1, 1);
		tex.SetPixels(new Color[]{Color.blue});

		CreateEventSystem (this.transform);

		GameObject a = area (this.transform, rect, tex);
		GameObject b1 = button (a.transform, new Vector2(0,12), "Click me", delegate {
			TaskOnClick();
		});
		GameObject b2 = button (a.transform, new Vector2(0,-12), "Or me",  delegate {
			TaskOnClick();
		});
	}

	public void TaskOnClick ()
	{
		Debug.Log ("Hello!");
	}

	// BOILER PLATE BELLOW...

	GameObject area (Transform parent, Rect rect, Texture2D tex)
	{
		// OBJECT
		GameObject canObj = new GameObject ("UI:Canvas");
		canObj.transform.SetParent (parent);

		// OBJECT:CANVAS
		// http://docs.unity3d.com/Manual/UICanvas.html
		Canvas can = canObj.AddComponent<Canvas> ();
		can.renderMode = RenderMode.ScreenSpaceOverlay;
		can.pixelPerfect = true;

		// OBJECT:CANVAS:SCALER
		// http://docs.unity3d.com/ScriptReference/UI.CanvasScaler.html
		CanvasScaler canObjSca = canObj.AddComponent<CanvasScaler> ();
		canObjSca.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
		canObjSca.referenceResolution = new Vector2(540, 480);

		// OBJECT:CANVAS:RAYCASTER
		// https://docs.unity3d.com/Manual/script-GraphicRaycaster.html
		GraphicRaycaster canvasRayc = canObj.AddComponent<GraphicRaycaster> ();	

		// OBJECT:CANVAS:PANEL
		GameObject panObj = new GameObject ("UI:Panel");
		panObj.transform.SetParent (canObj.transform);
		// https://docs.unity3d.com/Manual/UIBasicLayout.html
		RectTransform panTrs = panObj.AddComponent<RectTransform> ();
		panTrs.anchoredPosition = new Vector2 (rect.x, rect.y);
		panTrs.localScale = new Vector3 (1.0f, 1.0f, 1.0f);
		// SetSize(panTrs, new Vector2(rect.width, rect.height));

		// OBJECT:CANVAS:PANEL:TEXTURE
		Image img = panObj.AddComponent<Image> ();
		img.sprite = Sprite.Create (tex, new Rect (0, 0, tex.width, tex.height),
			new Vector2 (1.0f, 1.0f));

		return canObj;
	}

	GameObject button (Transform parent, Vector2 coord, string textStr, UnityAction eventListner)
	{
		Vector2 size = new Vector2 (90, 25);

		// OBJECT
		GameObject btnObj = new GameObject ("UI:Button");
		btnObj.transform.SetParent (parent);
		RectTransform btnTrs = btnObj.AddComponent<RectTransform> ();
		btnTrs.anchoredPosition = coord;
		btnTrs.localScale = new Vector3 (1.0f, 1.0f, 1.0f);
		SetSize(btnTrs, size);

		// OBJECT:CANVAS:TEXTURE
		// http://docs.unity3d.com/ScriptReference/Sprite.Create.html
		Image img = btnObj.AddComponent<Image> ();
		Texture2D tex = Resources.Load<Texture2D> ("button_bkg");
		img.type = Image.Type.Sliced;
		img.sprite = Sprite.Create (tex, new Rect (0, 0, tex.width, tex.height), new Vector2 (0.5f, 0.5f),
			100.0f, 0, SpriteMeshType.Tight, new Vector4 (10, 10, 10, 10));

		//  OBJECT:BUTTON
		// https://docs.unity3d.com/ScriptReference/UI.Button-onClick.html
		Button btn = btnObj.AddComponent<Button> ();
		btn.interactable = true;
		btn.onClick.AddListener (eventListner);
		// https://docs.unity3d.com/ScriptReference/UI.Selectable-transition.html
		btn.targetGraphic = img;
		btn.transition = Selectable.Transition.ColorTint;

		//  OBJECT:TEXT
		// https://docs.unity3d.com/ScriptReference/UI.Text.html
		GameObject btnTxtObj = new GameObject ("UI:Text");
		btnTxtObj.transform.SetParent (btnObj.transform);
		RectTransform btnTxtTrs = btnTxtObj.AddComponent<RectTransform> ();
		btnTxtTrs.anchoredPosition = new Vector2(0,0);
		// SetSize(btnTxtTrs, size);
		Text txt = btnTxtObj.AddComponent<Text> ();
		txt.supportRichText = true;
		txt.text = textStr;
		txt.fontSize = 12;
		txt.font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;
		txt.alignment = TextAnchor.MiddleCenter;
		txt.horizontalOverflow = HorizontalWrapMode.Overflow;
		txt.color = Color.black;

		return btnObj;
	}

	public 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));
	}

	public static GameObject CreateEventSystem (Transform parent)
	{
		// https://docs.unity3d.com/Manual/EventSystem.html
		GameObject esObj = new GameObject ("EventSystem");
		esObj.transform.SetParent (parent);

		EventSystem esClz = esObj.AddComponent<EventSystem> ();
		esClz.sendNavigationEvents = true;
		esClz.pixelDragThreshold = 5;

		StandaloneInputModule stdIn = esObj.AddComponent<StandaloneInputModule> ();
		stdIn.horizontalAxis = "Horizontal";
		stdIn.verticalAxis = "Vertical";

		TouchInputModule touchIn = esObj.AddComponent<TouchInputModule> ();

		return esObj;
	}
}

Demo

In this demo, we will test the two API side by side.

  • on UnityEditor Preview
    • test onClick action
    • test responsive
      • in low and high screen resolution
      • in portrait and landscape orientation
  • on Android Mobile Device

ScreenShot015.png

screenshot0053.png

ScreenShot016.png

ScreenShot017.png

ScreenShot006.png

ScreenShot010

ScreenShot011

ScreenShot003.png

screenshot0055.png


Source

https://github.com/DamienFremont/blog/tree/master/20170602-unity3d-use-ui-instead-of-gui-or-imgui/

References

https://unity3d.com/fr/learn/tutorials/topics/user-interface-ui

https://docs.unity3d.com/Manual/GUIScriptingGuide.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s