-
Notifications
You must be signed in to change notification settings - Fork 53
User Interface (UI)
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.
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 aCanvas
component itself. -
EventSystem EventSys
- the globalEventSystem
used by allUIBase
s. -
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.
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 callsUniversalUI.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
- theCanvas
component on yourRootObject
. -
PanelManager Panels
- handles yourPanelBase
instances
The PanelBase
class can be inherited from to create dragable, resizable panels. These panels will also recognise panels created by other UIBase
s, 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.
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
andRect.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
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 anImage
background Text
-
InputField
(wrapped in anInputFieldRef
) -
Button
(wrapped in aButtonRef
) Toggle
Slider
Scrollbar
Dropdown
- Custom components and more
UIFactory has some helpers for working with Unity UI elements, including:
-
SetLayoutElement(GameObject, ...)
- helper to easily create/get and set aLayoutElement
on any GameObject. -
SetLayoutGroup<T>(GameObject, ...)
- helper to easily create/get and set aHorizontalOrVerticalLayoutGroup
on any GameObject. This also uses Runtime-specific helpers so that all platforms and Unity versions are supported.
UniverseLib has implemented several custom UI control classes for more advanced UI features such as ScrollPools, and better Scrollbars.
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...
}
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
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.
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.
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);
}