Skip to content
Cem Ugur Karacam edited this page Sep 6, 2021 · 6 revisions

Motivation

Unity Editor allows us to design and create ui objects in any Scene. These objects need to be placed in a Canvas component and premade ui elements like Button and Text can be created in the active scene. Any MonoBehaviour object that lives in the scene can be saved as a Prefab . That allows us to use them in multiple scenes, instantiate in runtime or achieve nested prefabs and variants introduced in 2018.3 version.

unity_editor_ui_objects

This workflow is visually easy to prototype and design. Especially static elements in the scene like a background or a label that sits in the scene. But the dynamic control or runtime manipulation of scene elements via code can be overwhelming. Each element needs to be referenced before the application started from the scene or in the runtime, searching through the scene or any MonoBehaviour via GetComponent<T> methods.

public class SomeManager : MonoBehaviour
{
    //  If your class attached to an object before game started
    //  your components can be referenced like this from the editor
    public Button buttonFromScene;

    private void Start()
    {
        //  Find the object int the scene matching exact name
        //  Get the component via api
        Text scoreLabel = GameObject.Find("Score Label").GetComponent<Text>();
    }
}

This methodology leads us to create wrapper objects to hold these multiple references to

// It is possible to create a prefab that hold the referances
public class SomeView : MonoBehaviour
{
    public Button buttonFromPrefab;
    public Text buttonLabel;
    public Dropdown optionsDropdown;
}

public class SomeManager : MonoBehaviour
{
    ...
    private void CreateUI()
    {
        //  prefab gameObject could be loaded from Resources
        GameObject uiObject = Instantiate(uiPrefab, uiParent);
        SomeView ui = uiObject.GetComponent<SomeView>();
        
        ui.buttonLabel.text = "some label";
        ui.buttonFromPrefab.onClick.AddListener(SomeMethod);
    }

    private void SomeMethod()
    {
        //  Do stuff
    }
    ...
}

This example is the way I do most of the time when things get complex but ui programming may gets to complicated when your project grow, as we know, or you may only need a simple structure that only needs a couple of line of work and you don't feel like to hassle with unity ui and editor flow at all.

Another solution would be creation the objects programmatically. Since any component can be added to any GameObject it is possible to make any remade ui objects that editor provided.

...
GameObject uiObject = new GameObject();
Text label = uiObject.AddComponent<Text>();

label.text = "Hello!";
...

That works but you know that it must be placed in a Canvas and you would needed more when you make more complex objects like Button or Dropdown. Its just a bunch of nested objects and attached components like image, text and button.

button_hierarchy.png

This is how I started to create quill. I wanted to create the elements I need in one line without going back and fourth between editor and code, referencing and managing all objects and wrappers, so on. Quill does not try to replace Unity UI. It only provides an api to achieve the programmatic functionality I described above.

Here is the label and button:

    public class QuillLabel : Text, IQuillElement
    {
        ...
    }

quill_label.png

    public class QuillButton : Button, IQuillElement
    {
        ...
        public QuillLabel label;
        public QuillBox box;
        ...
    }

quill_button.png

So every quill component is derived from the unity ui class itself. Then you would use the way you use normally.

var label = Quill.CreateLabel("hello world!");
label.fontSize = 20;

var button = Quill.CreateButton("hello button!");
button.onClick.AddListener(SomeMethod);
button.interactable = false;

Benefits

Also, it exposes c# api to lua (MoonSharp). It will hook up Init and OnUpdate functions from main.lua file.

Quill Documentation

Clone this wiki locally