Scripts and codes that enhances or changes the GUI in the engine.

Steve_Yorkshire
Posts: 239
Joined: Tue Feb 03, 2015 10:30 pm

This is the gamepad friendly way of navigating through GUIs.

In stock Torque, you cannot access GuiGameListMenuCtrl because there is no default datablock for one to use as an example.

GuiGameListMenuCtrl: (null) can't be created with a profile of type GuiControlProfile. Please create it with a profile of type GuiGameListMenuProfile.<input> (0): Register object failed for object (null) of class GuiGameListMenuCtrl.

I found one posted at http://www.garagegames.com/community/forums/viewthread/136541/1#comment-856194 by @
andrewmac
who said that he had got it to appear but never to actually function. After a bit of faffing, I got it up and running - it's a bit autistic ...

How it works:
It creates a series of buttons (called rows) out of a button bitmap (remember TGEA's main menu?). So you need to set that up first.
EXCEPT you need to add your rows BEFORE the gui control is pushed to Canvas (unlike every other thing in Torque's Gui system it seems).

GuiGameListMenuCtrl: ListMenu can't be woken up without any rows. Please use "addRow" to add at least one row to the control before pushing it to the canvas.GuiControl::awaken: failed onWake for obj: 2036:GuiGameListMenuCtrl (ListMenu)

So to sort this out, I ended up creating a new gui which will be triggered instead of mainMenuGui, but has it's gamepad friendly rows/buttons set up in a gui/script file, thus is loaded before our gamepad friendly gui ever gets pushed to Canvas. If the addRow gets called again, it keep repeatedly adding the same rows and makes a right mess, so it needs to be done early and only done once.

First up, we need @
andrewmac
's gui profiles and a bitmap array. Hello TGEA start buttons!

Save this and stick it in art/gui folder.

Next up the data, stick this in somewhere like art/gui/customProfiles.cs.

singleton GuiGameListMenuProfile(DefaultListMenuProfile){   fontType = "Arial Bold";   fontSize = 20;   fontColor = "120 120 120";   fontColorSEL = "16 16 16";   fontColorNA = "200 200 200";   fontColorHL = "100 100 120";   HitAreaUpperLeft = "16 20";   HitAreaLowerRight = "503 74";   IconOffset = "40 0";   TextOffset = "100 0";   RowSize = "525 93";   bitmap = "art/gui/listMenuArray";   canKeyFocus = true;};singleton GuiGameListOptionsProfile(DefaultOptionsMenuProfile){   fontType = "Arial Bold";   fontSize = 20;   fontColor = "120 120 120";   fontColorSEL = "16 16 16";   fontColorNA = "200 200 200";   fontColorHL = "100 100 120";   HitAreaUpperLeft = "16 20";   HitAreaLowerRight = "503 74";   IconOffset = "40 0";   TextOffset = "90 0";   RowSize = "525 93";   ColumnSplit = "220";   RightPad = "20";   bitmap = "art/gui/listMenuArray";   canKeyFocus = true;};singleton GuiControlProfile(GamepadDefaultProfile){   border = 0;};singleton GuiControlProfile(GamepadButtonTextLeft){   fontType = "Arial Bold";   fontSize = 20;   fontColor = "40 40 40";   justify = "left";};singleton GuiControlProfile(GamepadButtonTextRight : GamepadButtonTextLeft){   justify = "right";};

Now create a new gui for our gamepad friendly buttons. Let's call it ListMenuGui.gui and the actual gui interface object is "ListMenu".

//--- OBJECT WRITE BEGIN ---%guiContent = new GuiBitmapCtrl(ListMenuGui) {   bitmap = "art/gui/background";   color = "255 255 255 255";   wrap = "0";   position = "0 0";   extent = "1024 768";   minExtent = "8 2";   horizSizing = "right";   vertSizing = "bottom";   profile = "GuiDefaultProfile";   visible = "1";   active = "1";   tooltipProfile = "GuiToolTipProfile";   hovertime = "1000";   isContainer = "1";   canSave = "1";   canSaveDynamicFields = "1";      new GuiBitmapCtrl() {      bitmap = "art/gui/Torque-3D-logo.png";      color = "255 255 255 255";      wrap = "0";      position = "540 30";      extent = "443 139";      minExtent = "8 2";      horizSizing = "left";      vertSizing = "bottom";      profile = "GuiDefaultProfile";      visible = "1";      active = "1";      tooltipProfile = "GuiToolTipProfile";      hovertime = "1000";      isContainer = "0";      canSave = "1";      canSaveDynamicFields = "1";         Enabled = "1";         isDecoy = "0";   };   new GuiGameListMenuCtrl(ListMenu) {      debugRender = "0";      position = "0 0";      extent = "320 240";      minExtent = "8 2";      horizSizing = "center";      vertSizing = "center";      profile = "DefaultListMenuProfile";      visible = "1";      active = "1";      CallbackOnA = "ListMenu.activateRow();";      tooltipProfile = "GuiToolTipProfile";      hovertime = "1000";      isContainer = "0";      canSave = "1";      canSaveDynamicFields = "0";   };};//--- OBJECT WRITE END ---

Note CallbackOnA = "ListMenu.activateRow();"; which means that A button will activate that row and fire off it's callback function. There are also CallbackOnB/X/Y too but we are only using button A in this test.

And our data for these rows/buttons and their callbacks are created in a new gui/script file: "scripts/gui/ListMenuGui.cs".

//yorks only load these things once or else you'll end up with multiples!!!!ListMenu.addRow("Play", "ListMenuOnPlay", 0);ListMenu.addRow("Join", "ListMenuOnJoin", 0);ListMenu.addRow("Options", "ListMenuOnOptions", 0);ListMenu.addRow("Quit", "ListMenuOnQuit", 0);function listMenuOnPlay(%this){   Canvas.pushDialog(ChooseLevelDlg);}function ListMenuOnJoin(%this){   Canvas.pushDialog(JoinServerDlg);}function ListMenuOnOptions(){   Canvas.pushDialog(OptionsDlg);}function ListMenuOnQuit(){   quit();  }

So here we add the rows which will be our new buttons in ListMenuGui. The first var (Play, etc) is the button/row text, and the second var (ListMenuOnPlay, etc) is the callback which will trigger a function.

Also note the functions at the end of the file. These are the callbacks, but they only callback to non-class functions, unlike everything else in Torque which callbacks to it's own class; eg: function PlayerData::onDamage(). We have 4 buttons, the standard quadrinity (not sure that's really a word) of SinglePlayer, Multiplayer, Options, Quit.

Don't forget to initialize the new file. scripts/client/init.cs after the stock Guis (remember datablocks before guis!).

//...   // Load up the shell GUIs   exec("art/gui/mainMenuGui.gui");   exec("art/gui/joinServerDlg.gui");   exec("art/gui/endGameGui.gui");   exec("art/gui/StartupGui.gui");   exec("art/gui/chooseLevelDlg.gui");   exec("art/gui/loadingGui.gui");   exec("art/gui/optionsDlg.gui");   exec("art/gui/remapDlg.gui");      //custom GUIs yorks   exec("art/gui/ListMenuGui.gui");//yorks our replacement for mainMenuGui      // Gui scripts   exec("./playerList.cs");   exec("./chatHud.cs");   exec("./messageHud.cs");   exec("scripts/gui/playGui.cs");   exec("scripts/gui/startupGui.cs");   exec("scripts/gui/chooseLevelDlg.cs");   exec("scripts/gui/loadingGui.cs");   exec("scripts/gui/optionsDlg.cs");      //custom Gui scripts yorks   exec("scripts/gui/ListMenuGui.cs");//yorks this is our gamepad gui callback data   // Client scripts   exec("./client.cs");//...

Now let's override mainMenuGui with our new gui. Because MainMenuGui gets pushed from a few places in script (like when you disconnect) the easiest way to override it is to get it to push our new Gui itself. art/gui/mainMenuGui.gui

//--- OBJECT WRITE END ---function MainMenuGui::onWake(%this){   if (isFunction("getWebDeployment") &&       getWebDeployment() &&       isObject(%this-->ExitButton))      %this-->ExitButton.setVisible(false);         Canvas.pushDialog(ListMenuGui);//yorks added so we don't have to hunt around}

And that's it. Our new gui loads instead of the MainMenu. The D-Pad on the controller moves the highlighted/selected button up and down automatically without any actionMaps. GamePad A Button does the same as mouse onClick() would - and remember these controls can also use the mouse. There is also a GuiGameListOptionsCtrl which I imagine has a lot more moving around - but I haven't experimented with that one yet - though I expect it will also look like the old TGEA one.

Obviously as this is only a replacement for the mainMenu, you have to go back to using the mouse on all the other GUIs, once activated from the gamepad.

That's all folks!

[EDIT] Good job I copy/pasted this to notepad when I'd finished cos the forum timeout booted me and lost it all
Last edited by Steve_Yorkshire on Wed Mar 21, 2018 12:52 am, edited 1 time in total.

Steve_Yorkshire
Posts: 239
Joined: Tue Feb 03, 2015 10:30 pm

Righty-ho, I've done a load more research on both GuiGameListMenuCtrl and GuiGameListOptionsCtrl and now understand how they work.

GuiGameListMenuCtrl let's you go up and down a single column of buttons (called rows) and has 4 available callbacks A/B/X/Y. 1 of these callbacks is needed to activate the selected button, so the other 3 could be used for "other stuff" like press B to pop the current dialog, etc.

GuiGameListOptionsCtrl allows the use of left/right with the gamepad to cycle through various options. Eg: screen resolution.

The downside to this functionality is that it exists only vertically, and only a single instance can be accessed in a Gui.gui file. You can build multiple Lists, but only the last one to load in the Gui file is accessable and you can't move between them. So it can't use horizontally displayed tabs.

If you wanted to recreate the current optionsDlg (which has tabs for GRAPHICS/AUDIO/CONTROLS) using this system, you'd have to have a "master" page with 3 rows/buttons for each that then pushed a new gui with the relevant features - again, all in 1 vertical column.

I even downloaded TGEA to have a look at how it does it - boy are those editors ugly