User Interface
In UNIGINE a Graphical User Interface (GUI) is composed of different types of widgets added to it. Basically, there are two ways of creating GUI:
- By adding widgets to the system GUI (UNIGINE user interface) that is rendered on top of application window. In this case we use the Gui class.
- By adding widgets to a GUI object positioned in the world. In this case, any postprocessing filter can be applied. By creating a specific Gui object (or GuiMesh for arbitrary geometry) that can be placed anywhere in the scene, and adding widgets to it. This can be useful, for example, to implement interaction with the interface displayed on a computer screen in a room (i.e. UI bound to an object that can be viewed from different angles). In this case, a post-processing filter can be applied.
There are 2 ways to create the GUI layout:
- Directly from code via GUI classes
- Using UI files with the description of user interface. Such a description file in XML-like format can be created for a complex interface to write less code.
The following code demonstrates how to add Label and Slider widgets to the system GUI:
// getting a reference to the system GUI
Gui gui = Gui.GetCurrent();
// creating a label widget and setting up its parameters
WidgetLabel widget_label = new WidgetLabel(gui, "Label text:");
widget_label.SetToolTip("This is my label!");
widget_label.Arrange();
widget_label.SetPosition(10, 10);
// creating a Slider widget and setting up its parameters
WidgetSlider widget_slider = new WidgetSlider(gui, 0, 360, 90);
widget_slider.SetToolTip("This is my slider!");
widget_slider.Arrange();
widget_slider.SetPosition(100, 10);
// adding widgets to the system GUI
gui.AddChild(widget_label, Gui.ALIGN_OVERLAP | Gui.ALIGN_FIXED);
gui.AddChild(widget_slider, Gui.ALIGN_OVERLAP | Gui.ALIGN_FIXED);
In order to use GUI elements (not just watch them rendered) we must specify handlers for various events (click, change, etc.). The following code demonstrates how to set event handlers:
void onSliderChanged(Widget sender)
{
Log.Message("\n The value of the slider has been changed: {0}\n", (sender as WidgetSlider).Value);
}
private void Init()
{
// getting the reference to the system GUI
Gui gui = Gui.GetCurrent();
// creating the Button widget and setting its parameters
WidgetButton widget_button = new WidgetButton(gui, "Press me");
widget_button.Arrange();
widget_button.SetPosition(10,10);
// creating the Slider widget and setting its parameters
WidgetSlider widget_slider = new WidgetSlider(gui);
widget_slider.Arrange();
widget_slider.SetPosition(100,10);
// setting a lambda function to handle CLICKED event (mouse click)
widget_button.EventClicked.Connect(() => Log.Message("Button is pressed\n"));
// setting a method to handle CHANGED event (changing)
widget_slider.EventChanged.Connect(onSliderChanged);
// adding the created widgets to the system GUI
gui.AddChild(widget_button, Gui.ALIGN_OVERLAP | Gui.ALIGN_FIXED);
gui.AddChild(widget_slider, Gui.ALIGN_OVERLAP | Gui.ALIGN_FIXED);
}
Practice#
In architectural visualization projects, a widespread option is changing materials on various objects, for example, to preview different wallpapers on the walls and match them with the decor of the furniture. For our project, let's create a component (also inherit it from Interactable) that displays a list of available materials for a selected object in the UI based on ObjectGui with the ability to select and automatically apply them.
-
Create a new component called MaterialCustomizer and add the following code to it:
using System; using System.Collections; using System.Collections.Generic; using Unigine; [Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- this line is generated automatically for a new component public class MaterialCustomizer : Interactable { [Parameter(Tooltip = "List of materials for object customization")] public List<Material> MaterialsList = null; private ObjectGui gui = null; // GUI object for displaying of the material selection interface // the function that assigns the material selected in the menu to the selected customized object's surface private void select_material_handler(Widget w) { (node as Unigine.Object).SetMaterial(MaterialsList[(w as WidgetComboBox).CurrentItem],ComponentSystem.FindComponentInWorld<InputProcessor>().intersection.Surface); } private void Init() { // setting the text of the tooltip that will be displayed when the cursor hovers over the object tooltip = "Right-click displays a menu where you can select a material for the object."; // creating the GUI object to display the material selection interface and setting its size and resolution gui = new ObjectGui(0.5f, 0.5f); gui.SetScreenSize(600, 600); // enabling intersection detection to interact with UI using the mouse gui.SetIntersection(true, 0); // making the interface always rotate to the user and not blocked by other objects gui.Billboard = true; gui.GetMaterialInherit(0).DepthTest = false; // disabling the black background and temporarily hiding the UI gui.Background = false; gui.Enabled = false; // setting the distance from which interaction with the interface is possible gui.ControlDistance = 10; if(MaterialsList != null && MaterialsList.Count > 0) { // adding the WidgetVBox widget container for vertical layout of elements, setting indents WidgetVBox vbox = new WidgetVBox(gui.GetGui()); vbox.SetSpace(20,20); // enabling the background and setting its color vbox.Background = 1; vbox.BackgroundColor = new vec4(0.1f,0.5f,0.1f,0.4f); // creating a WidgetLabel widget to display the header, setting its position, and font size WidgetLabel label = new WidgetLabel(gui.GetGui(), "Select the material:"); label.FontSize = 50; // adding the Label widget to the container vbox.AddChild(label,Gui.ALIGN_TOP); // creating a WidgetComboBox widget to display a drop-down list of materials WidgetComboBox mat_menu = new WidgetComboBox(gui.GetGui()); mat_menu.FontSize = 40; // setting the handler on selection of an item from the list mat_menu.EventChanged.Connect(select_material_handler); // adding items to the menu according to the list of materials for(int i = 0; i < MaterialsList.Count; i++) { string str = FileSystem.GetVirtualPath(MaterialsList[i].Path); mat_menu.AddItem(str.Substring(str.LastIndexOf("/")+1)); } // adding the ComboBox widget to the container vbox.AddChild(mat_menu,Gui.ALIGN_TOP); // adding container widget to GUI gui.GetGui().AddChild(vbox,Gui.ALIGN_EXPAND|Gui.ALIGN_OVERLAP); } } public override void Action(int num) { // action indices different from zero are invalid for this component, so they are ignored if (num != 0) return; // placing the GUI near the click point gui.WorldPosition = ComponentSystem.FindComponentInWorld<InputProcessor>().intersection.Point; // hide the GUI for all other customizable objects (if any) foreach(MaterialCustomizer customizer in ComponentSystem.FindComponentsInWorld<MaterialCustomizer>() ) if (this != customizer) customizer.gui.Enabled = false; // show or hide the interface of material selection for the object gui.Enabled = !gui.Enabled; } }
- Assign our new component to the bedside_table_1 object in our scene.
-
Fill in the list of materials. To do this, change the number of items in the list from 0 to 3 and then drag three materials into the corresponding fields.
Upon right-clicking an object a list of available materials will be displayed, you can choose the desired one, and it will be applied automatically right as you click it: