Facebook Twitter YouTube Frictional Games | Forum | Privacy Policy | Dev Blog | Dev Wiki | Support | Gametee


Thread Rating:
  • 3 Vote(s) - 3.67 Average
  • 1
  • 2
  • 3
  • 4
  • 5
GUI Tutorial Series
Abion47 Offline
Senior Member

Posts: 369
Threads: 22
Joined: Oct 2015
Reputation: 46
#10
RE: GUI Tutorial Series

Who's Got The Button

Up until now, we've been making terminals that have been capable of drawing text in a variety of forms, as well as being able to draw images. This is great and all when you want to display some information quickly, but all the memorable terminals in SOMA involve the player actually being able to navigate around, performing actions, and taking names. So let's start swinging this series around and learn how to make our terminals interactive.

Table of Contents
The Basics
Getting More Advanced
Using SOMA's Built-In GUI Styles
  • StationGui (WIP)
  • UrbanGui (WIP)
  • Playing Audio (WIP)

Going Beyond Terminals
  • Setting Up a User Module (WIP)
  • Basic Heads-Up Display (WIP)
  • Target Info Module (WIP)
  • Player HUD Menu System (WIP)

Tutorial Requirements

For this tutorial, you will need the following:
  • A map with a prepared terminal, plus all the necessities.
  • A script file with a prepared OnGui terminal callback function.

Tutorial Source Files

Initial File
Completed File

The Tutorial Itself

The first thing we need to do is to head back to the Level Editor. When we created our terminal back in Lesson Zero, it's default setting was to disable player interaction. That's been fine so far, but we're going to need that interaction now.

Select your terminal entity, and under the Terminal tab, enable the AllowInteraction box.

[Image: JFkvSZF.png]

That's all we need here, so save your map and head over to the script.

So far, we've seen three types of widgets - the Label, the TextFrame, and the Image. Here, we are going to introduce the fourth basic widget, the Button. Down in your OnGui function, insert the following code:

cImGuiButtonData buttonData;
buttonData.mFont.mvSize = cVector2f(60, 60);
buttonData.mColorBase = cColor(0.35, 0.35, 0.35);
buttonData.mFont.SetFile("sansation_large_bold.fnt");
            
bool bButtonState = ImGui_DoButtonExt("gui_button", "Press Me", buttonData, cVector3f(150, 190, 0), cVector2f(300, 80));

Alright, let's take this one through the motions.

First up, you've got your button widget data, the cImGuiButtonData. Like the others, this holds the button properties that we are going to use later on.

The next couple of lines has the usual - changing the font size so our text isn't tiny, and changing the background color of the button. Like the TextFrame, both the button's text color and background color default to white, and that isn't condusive to a good GUI experience. Here, we change the color to a slightly dark gray.

This next line is a bit new though. What the mFont's function SetFile does is it changes what font we are using for the text. The default font is alright for doing text, but when you use this font for a button, it skews the button text toward the bottom of the button, potentially pushing it off the button altogether. So we want to change the font to something that correctly positions the text in the center of the button.

Finally we have our ImGui_DoButtonExt function. So far, the ImGui functions we've been using to create our widgets have been straight-forward, but the Button function is a bit special. What it does is it returns a boolean value representing whether the Button has been pressed. If so, it returns true, otherwise it returns false.

Now we've created our Button, but as you see all we've done is capture whether the Button has been pressed - we haven't done anything that would actually be triggered by the Button. Let's fix that by putting in the following right after our Button code:

if (bButtonState)
{
    cImGuiLabelData labelData;
    labelData.mFont.mvSize = cVector2f(100, 100);
    
    ImGui_DoLabelExt("You pressed it", labelData, cVector3f(150, 450, 0));
}

Nothing too fancy. We check whether the Button was pressed through use of the variable bButtonState, and if it's true, we have ImGui put up a Label saying we did so.

If you were to run this code right now, you would be able to see your button in all it's glory.

However, there's a problem. Clicking our button makes the Label appear, but only for a fraction of a second. The reason for this requires a bit of background on how ImGui works.

You may or may not have noticed by now, but our OnGui function is being called many times per second. On each call, we are creating our widgets repeatedly, and essentially building our GUI from scratch every time. This is by design, but it can be a bit of a difficult concept to wrap your head around. But the result is that when we press our button in game, ImGui registers that press in the frame that it happened and creates the Button accordingly.

However, when we get to the next frame, we leave the frame in which the Button press was triggered, so it doesn't get registered again, causing ImGui_DoButtonExt to return false. This is what causes our Label to blink in and out of existence.

What we are going to need is to store the fact that a Button press happened in a way that retains the Button press even after the OnGui function completes its rounds. To accomplish this, we are going to use a member variable.

Scroll up to the section in your code that reserves space for your terminal variables, and put in the following:

bool mbButtonPressed;

Now scroll back down to the OnGui function and change up the body of our if statement block into this:

if (bButtonState)
{
    mbButtonPressed = true;
}

Then finally, underneath that if statement block, let's add another one:

if (mbButtonPressed)
{
    cImGuiLabelData labelData;
    labelData.mFont.mvSize = cVector2f(100, 100);
    
    ImGui_DoLabelExt("You pressed it", labelData, cVector3f(150, 450, 0));
}

At this point, we have two different boolean variables storing the state of our Button, but where bButtonState stores whether the Button was pressed in this frame, mbButtonPressed stores whether the Button has been pressed at all. This second boolean is what we want when we are deciding if we are going to show our Label, so we move the code to create the variable into the second if block.

If we run our map now, it should look similar to last time. However, instead of our Label blinking, it will now stick around after the Button gets pressed.

[Image: GvRROIH.jpg]

This is just about perfect, but one thing is still missing. As you can see, our Button's appearance is quite static. Hovering the mouse over it or clicking it doesn't change what it looks like. In fact, if the Label didn't appear, we wouldn't be having any indication that it was doing anything at all.

To change this, we are going to need to add a bit more information to our Button data. Underneath the code where we set buttonData's font file, add the following:

buttonData.mbUseInFocusColor = true;
buttonData.mColorInFocus = cColor(0.25, 0.25, 0.25);
buttonData.mbUseTriggeredColor = true;
buttonData.mColorTriggered = cColor(0.1, 0.1, 0.1);

What we have here is that we are changing the Button's background color based on what we are currently doing with it.

When the Button is in focus, that means the cursor is hovering over the Button, but hasn't pressed it yet. We want to change the color in this event to confirm to the player that what they are looking at is a Button that can be pressed, so here we will set it to a slightly darker gray than before.

When the Button is triggered, that means the cursor has the button pressed. This is to tell the player that the action they performed is doing something, and we will set it to the darkest gray yet so it is easily differentiated from the earlier two grays.

Another important thing we are doing is setting the buttonData's mbUseInFocuscolor and mbUseTriggeredColor to true. What this does is tell ImGui that there are values in mColorInFocus and mColorTriggered that we want it to use. If you don't set the flags to true, any color information you put in those fields will be ignored, so don't forget to do that.

Let's go ahead and save our script, then check our map in the DevBat.

[Image: q5i4ls4.jpg]

As you can see, our Button now behaves exactly how we would expect a Button to behave. Hovering over it hames it dim in response, then pressing it gives feedback in the form of the triggered color as well as making our Label appear.

This marks the end of the first section of this tutorial series. You now know how to set up your terminal to receive player interaction, as well as draw the four basic widgets - Label, TextFrame, Image, and Button. Using a combination of these four widgets will enable you to create all of the common terminal interfaces.

In the next section, we are going to dive into some of the more advanced widgets and GUI concepts, so stick around for that. Smile
(This post was last modified: 09-03-2016, 08:04 PM by Abion47.)
11-09-2015, 02:57 AM
Find


Messages In This Thread
GUI Tutorial Series - by Abion47 - 11-07-2015, 09:27 AM
RE: GUI Tutorial Series - by Abion47 - 11-07-2015, 10:44 AM
RE: GUI Tutorial Series - by Abion47 - 11-07-2015, 11:11 AM
RE: GUI Tutorial Series - by Kanthos - 11-07-2015, 12:38 PM
RE: GUI Tutorial Series - by RaideX - 11-07-2015, 01:28 PM
RE: GUI Tutorial Series - by Abion47 - 11-07-2015, 02:52 PM
RE: GUI Tutorial Series - by Abion47 - 11-08-2015, 03:38 AM
RE: GUI Tutorial Series - by A.M Team - 11-08-2015, 12:46 PM
RE: GUI Tutorial Series - by Abion47 - 11-09-2015, 01:28 AM
RE: GUI Tutorial Series - by Abion47 - 11-09-2015, 02:57 AM
RE: GUI Tutorial Series - by Abion47 - 11-10-2015, 05:06 AM
RE: GUI Tutorial Series - by Vale - 11-10-2015, 08:00 AM
RE: GUI Tutorial Series - by Abion47 - 11-10-2015, 08:03 AM
RE: GUI Tutorial Series - by Abion47 - 11-27-2015, 01:20 AM
RE: GUI Tutorial Series - by Abion47 - 07-12-2016, 07:52 PM
RE: GUI Tutorial Series - by Abion47 - 07-12-2016, 10:04 PM
RE: GUI Tutorial Series - by NewPueblo - 09-03-2016, 12:09 AM
RE: GUI Tutorial Series - by Abion47 - 09-03-2016, 08:02 PM



Users browsing this thread: 1 Guest(s)