Skip to content
This repository has been archived by the owner on May 9, 2023. It is now read-only.

User Interface (UI)

Sinai edited this page Apr 27, 2022 · 16 revisions

UniverseLib's UI features live in the UniverseLib.UI namespace. The UI is built on top of Unity's uGUI framework (UnityEngine.UI), with some additional helpers and wrapper classes to make things easier.

It is recommended that you are somewhat familiar with Unity UI before using UniverseLib's UI systems, as it is presumed you have an understanding of how a GameObject-driven UI works. See the documentation here. Note however that we are using C# to create and control the UI as opposed to the Unity Editor, so it will be more like working with the DefaultControls class.

UniversalUI

The UniverseLib.UI.UniversalUI class handles all UIBase instances. Using the UIBase system is required if you wish to make use of UniverseLib's UI Input support.

To create a UIBase, use UniversalUI.RegisterUI. The simplest implementation would look like this:

public static UIBase UiBase { get; private set; }

void Universe_OnInitialized()
{
    UiBase = UniversalUI.RegisterUI("my.unique.ID", UiUpdate);
}

void UiUpdate()
{
    // Called once per frame when your UI is being displayed.
}

In the UniversalUI class you will also find:

  • GameObject CanvasRoot - the root object which all UiBase canvases live on. It does not actually contain a Canvas component itself.
  • EventSystem EventSys - the global EventSystem used by all UIBases.
  • Font ConsoleFont - an instance of the Consola font, provided the AssetBundle was loaded successfully. Otherwise this will be Arial.
  • Font DefaultFont - Arial, or the default font of the game.

UIBase

The UIBase returned from UniversalUI.RegisterUI is a wrapper for your UI Canvas. It simply contains:

  • bool Enabled - use this to set your UI active or disabled. This calls UniversalUI.SetUIActive(ID, value);
  • GameObject RootObject - the root object of your UI. All UI objects should be added onto this or a child of this.
  • Canvas Canvas - the Canvas component on your RootObject.
  • PanelManager Panels - handles your PanelBase instances

PanelBase

The PanelBase class can be inherited from to create dragable, resizable panels. These panels will also recognise panels created by other UIBases, any will reorder themselves (along with the UIBase) when clicked on.

public class MyPanel : UniverseLib.UI.Panels.PanelBase
{
    public MyPanel(UIBase owner) : base(owner) { }

    public override string Name => "My Panel";
    public override int MinWidth => 100;
    public override int MinHeight => 200;
    public override Vector2 DefaultAnchorMin => new(0.25f, 0.25f);
    public override Vector2 DefaultAnchorMax => new(0.75f, 0.75f);
    public override bool CanDragAndResize => true;

    protected override void ConstructPanelContent()
    {
        Text myText = UIFactory.CreateLabel(ContentRoot, "myText", "Hello world");
        UIFactory.SetLayoutElement(myText.gameObject, minWidth: 200, minHeight: 25);
    }

    // override other methods as desired
}

To create an instance of your panel class, simply call the constructor and UniverseLib will do the rest.

UIBase myUIBase = UniversalUI.RegisterUI("me.mymod", MyUpdateMethod);
MyPanel myPanel = new(myUIBase);
  • Note: The PanelBase class was designed with the assumption that you will create a separate class for each panel instance, and not reuse the same class for multiple panels. However there is nothing stopping you from doing so if you want to implement it that way, it should work fine.

Manually resizing panel

You can manually resize your PanelBase by using the Rect property - a reference to the panel's base RectTransform.

If you manually change your panel size you should also call myPanel.Dragger.OnEndResize(); to ensure the resize triggers update, and listeners to the onFinishedResize event are notified.

  • Set Rect.anchorMin and Rect.anchorMax to set a percentage value from 0.0f to 1.0f
  • Use Rect.SetSizeWithCurrentAnchors to set a pixel value for the horizontal or vertical axis.
  • Remember to call myPanel.Dragger.OnEndResize() afterwards regardless of which method you use.

UIFactory

UIFactory contains methods for creating actual UI elements, and performing some common operations on them. It is loosely based on Unity's DefaultControls class.

UIFactory can help you create:

  • Basic *LayoutGroup objects with an Image background
  • Text
  • InputField (wrapped in an InputFieldRef)
  • Button (wrapped in a ButtonRef)
  • Toggle
  • Slider
  • Scrollbar
  • Dropdown
  • Custom components and more

Helpers

UIFactory has some helpers for working with Unity UI elements, including:

  • SetLayoutElement(GameObject, ...) - helper to easily create/get and set a LayoutElement on any GameObject.
  • SetLayoutGroup<T>(GameObject, ...) - helper to easily create/get and set a HorizontalOrVerticalLayoutGroup on any GameObject. This also uses Runtime-specific helpers so that all platforms and Unity versions are supported.

Custom controls

UniverseLib has implemented several custom UI control classes for more advanced UI features such as ScrollPools, and better Scrollbars.

ScrollPool

The ScrollPool class is an implementation of an object-pooled ScrollView component, using any ICell implementation for pooling. A ScrollPool must be initialized and controlled with an ICellPoolDataSource.

public class MyCell : ICell
{
    // Implement interface members...
}

public class MyCellHandler : ICellPoolDataSource<MyCell>
{
    GameObject myPanel;
    GameObject panelContent;
    ScrollPool<MyCell> ScrollPool;

    void ConstructUI()
    {
        myPanel = UIFactory.CreatePanel("MyPanel", UiBase.RootObject, out panelContent, Color.black);

        ScrollPool = UIFactory.CreateScrollPool<MyCell>(panelContent, "MyScrollPool", out GameObject scrollRoot, out GameObject scrollContent, Color.blue);
        ScrollPool.Initialize(this);
    }

    // Implement interface members...
}

TransformTree

The TransformTree class is used to create a Transform hierarchy panel, similar to the scene hierarchy in the Unity Editor.

It requires that you first set up a ScrollPool<TransformCell> and pass that to the constructor.

Importantly, you must call TransformTree.RefreshData whenever you want to update the tree, it will not update automatically. It is recommended to not call RefreshData more than once per second, as it can be a somewhat expensive method if there are a lot of Transforms being displayed.

transformScrollPool = UIFactory.CreateScrollPool<TransformCell>(parent, "TransformTree", out GameObject transformObj,
    out GameObject transformContent, new Color(0.11f, 0.11f, 0.11f));

TransformTree = new TransformTree(transformScrollPool, GetTransformEntries, OnTransformCellClicked);

IEnumerable<GameObject> GetTransformEntries()
{
  // ...
}

void OnTransformCellClicked(GameObject obj)
{
  // ...
}
float timeOfLastTransformTreeUpdate;
void Update()
{
    if (Time.realtimeSinceStartup - timeOfLastTransformTreeUpdate > 1f)
    {
        timeOfLastTransformTreeUpdate = Time.realtimeSinceStartup;
        TransformTree.RefreshData(true, false, false, false);
    }
}

AutoSliderScrollbar

AutoSliderScrollbar is a simple helper class for a more robust Scrollbar. It takes a Slider-Scrollbar created with UIFactory.CreateSliderScrollbar and automates the functionality behind it.

Generally you will only use this class when it is returned to you via functions like UIFactory.CreateScrollView, or UIFactory.CreateScrollInputField. For examples of how to set up an AutoSliderScrollbar, see the UIFactory source.

InputFieldScroller

An InputFieldScroller uses an AutoSliderScrollbar to handle scrolling of an InputField, since Unity's implementation is far from perfect.

Generally you will only use this class when returned via UIFactory.CreateScrollInputField.

It has an OnScroll event you can subscribe to if you wish.

Object Pooling

UniverseLib has a complete object-pooling implementation which you can use if you wish, in UniverseLib.UI.ObjectPool.Pool.

The Pool<T> class creates and manages an object pool for any IPooledObject implementation.

public class MyPoolableObject : IPooledObject
{
    public GameObject UIRoot { get; set; }

    // Used by ScrollPool. Not necessary if your object is not an ICell.
    public float DefaultHeight => -1;

    public GameObject CreateContent(GameObject parent)
    {
        UIRoot = UIFactory.CreateUIObject(...);
    }
}

static void Example()
{
    var borrowed = Pool<MyPoolableObject>.Borrow();
    Pool<MyPoolableObject>.Return(borrowed);
}