Jump to content

Hacky GamePad Controllable Gui System


Steve_Yorkshire

Recommended Posts

I wanted to be able to control GUIs with a gamepad rather than a mouse (think Steam Big Screen etc). So, after looking at GuiGameListMenuCtrl I am ... absolutely none the wiser ...


Thus, I knocked up a quick and hacky - with the stress on hacky - solution that allows me navigate GUI buttons with a gamepad. Note: Currently this is working only in a vertical up/down single column manner.


So let's take the Main Menu. We're going to create an array with the buttons in THE ORDER THEY APPEAR ON SCREEN or it won't work. When we move up or down the array, we will move up and down the column of buttons.


But before this, let's actually name the buttons and add a marker image to each of them so we can see what is selected. Each button is made a container, and the marker image is a child of that button. This means that it covers the button slightly. The image for the markers will be "core/art/gui/images/dropslider_d.png".


Here is what the new Main Menu looks like in the GuiEditor. The buttons have names and each one a child marker.

new_mm_editor.png


Now for that array I was talking about.


The gamepad will control the array/buttons via a new ActionMap called "gamePadGuiMap".

We're going to use a $GLOBAL (Ancaps please stop foaming at the mouth) to keep track of WHICH GUI'S ARRAY IS CURRENTLY ACTIVE - because we might be doing this for all our GUIs if we want them to be accessible via the gamePad, and not just the MainMenu.

When input happens, we will update the array with the function updateLocalGamePadArray(%index);

function MainMenuGui::onWake(%this)
{
   //====================================
   //for gamepad control
   //====================================
 
   //place the cursor top left to get it out the way
   Canvas.setCursorPos("64 64");
 
   //let us use the gamepad for the GUI
   gamePadGuiMap.push();
 
   //make sure we have an array list of the buttons IN THE CORRECT ORDER
   if(!isObject(mainMenuArray))
   {
      echo("\c1creating new mainMenuArray");
      %list = new ArrayObject(mainMenuArray);
      %list = %list.getID();//passing names is silly
 
      //add the buttons in the CORRECT ORDER as they appear on screen
      %list.add(MainMenuPlay, 0);//0
      %list.add(MainMenuJoin, 0);//1
      %list.add(MainMenuOptions, 0);//2
      %list.add(MainMenuGuiEditor, 0);//3
      %list.add(MainMenuWorldEditor, 0);//4
      %list.add(MainMenuExit, 0);//5
 
      //%list.echo();//6 in total
   }
   else
      %list = mainMenuArray.getId();
 
   //Store as ebil globalist ...
   $activeGamePadGuiArray = %list;//every new gamepad gui needs this setting for itself onWake
 
   //update and reset
   updateLocalGamePadArray(0);
}
 
function MainMenuGui::onSleep(%this)
{
   //pop the gamePad Gui Action Map
   gamePadGuiMap.pop();
   $activeGamePadGuiArray = 0;//reset
}

 

When we use input from the gamePad, the array and thus GUI gets updated. We also wrap around the array if we move forwards at the end or reverse from the beginning. We hide the markers for unselected buttons, so only the selected button/index is shown.

function updateLocalGamePadArray(%num)
{
   //make global a local var
   %list = $activeGamePadGuiArray;
 
   //how many things are we dealing with here?
   %listNum = %list.count();
   %trueNum = %listNum - 1;
 
   echo("updateLocalGamePadArray " @ %list.getName() @ " count " @ %listNum @ " index " @ %num );
 
   //safety
   //return to 0 if we are at the end of the array
   if(%num > %trueNum)
      %num = 0;
 
   //safety
   //move to end of array if we are less than index 0
   if(%num < 0)
      %num = %trueNum;
 
   //set the first button to value 1 for selected
   for(%i =0; %i < %listNum; %i++)
   {
      //get the button ID
      %key = %list.getKey(%i);
 
      echo("index / key " @ %i SPC %key);
 
      //get the button child marker. This is the first (and only) object
      %child = %key.getObject(0);
 
      //add child's ID to value with button's key
      //%list.setValue(%i, %childId);
 
      //hide all but the first
      if(%i != %num)
         %child.setVisible(false);
      else
         %child.setVisible(true);
   }
 
   //use this var for storing which array key is selected. Default to 0, first button.
   %list.gamePadSelect = %num;
 
   echo("gamePadSelect = " @ %num);
}

 

Next up, we need to make the buttons actually do something when the active/selected button is activated.

function MainMenuPlay::onAction(%this)
{
   Canvas.pushDialog("ChooseLevelDlg");
}
 
function MainMenuJoin::onAction(%this)
{
   Canvas.pushDialog("JoinServerDlg");
}
 
function MainMenuOptions::onAction(%this)
{
   Canvas.pushDialog("OptionsDlg");
}
 
function MainMenuGuiEditor::onAction(%this)
{
   GuiEdit();
}
 
function MainMenuWorldEditor::onAction(%this)
{
   toggleEditor(1);
}
 
function MainMenuExit::onAction(%this)
{
   quit();
}

 

Of course this does not stop the mouse from intefering. So to use both mouse and gamepad together, we make sure that mouseOver a button also effects the gamepad array/selection. As we know the indexes of the buttons, we can call them directly.

function MainMenuPlay::onMouseEnter(%this)
{
   echo("MainMenuPlay: array index 0");
   updateLocalGamePadArray(0);
}
 
function MainMenuJoin::onMouseEnter(%this)
{
   echo("MainMenuJoin: array index 1");
   updateLocalGamePadArray(1);
}
 
function MainMenuOptions::onMouseEnter(%this)
{
   echo("MainMenuOptions: array index 2");
   updateLocalGamePadArray(2);
}
 
function MainMenuGuiEditor::onMouseEnter(%this)
{
   echo("MainMenuGuiEditor: array index 3");
   updateLocalGamePadArray(3);
}
 
function MainMenuWorldEditor::onMouseEnter(%this)
{
   echo("MainMenuWorldEditor: array index 4");
   updateLocalGamePadArray(4);
}
 
function MainMenuExit::onMouseEnter(%this)
{
   echo("MainMenuExit: array index 5");
   updateLocalGamePadArray(5);
}

 

Add the functions for moving up and down the column of buttons and button array.

function updateLocalGamePadArrayUp()
{
   //get the currently active gamepad gui button array
   %list = $activeGamePadGuiArray;
 
   %listNum = %list.count();
   %num = %listNum--;//remember count starts at 1 but array starts at zero
 
   %cur = %list.gamePadSelect;//current
 
   if(%cur == 0)
   {
      //cycle backwards to end
      %cur = %num;
   }
   else
   {
      %cur--;
   }
 
   updateLocalGamePadArray(%cur);
}
 
function updateLocalGamePadArrayDown()
{
   //get the currently active gamepad gui button array
   %list = $activeGamePadGuiArray;
 
   //%cur = %list.getCurrent();
   %listNum = %list.count();
   %num = %listNum--;//remember count starts at 1 but array starts at zero
 
   %cur = %list.gamePadSelect;//current
 
   if(%cur == %num)
   {
      //cycle forwards to start
      %cur = 0;
   }
   else
   {
      %cur++;  
   }
 
   updateLocalGamePadArray(%cur);
}
 
function updateLocalGamePadArraySelect()
{
   //get the currently active gamepad gui button array
   %list = $activeGamePadGuiArray;
 
   //get the active button
   %cur = %list.gamePadSelect;//current index
   %button = %list.getKey(%cur);
 
   //use button.onClick/onAction
   echo("using button via gamePad " @ %button SPC %list SPC %cur);
   %button.onAction();
}

 

And last but not least, we need an ActionMap for controlling the GUI via the gamepad and associated functions.


At the bottom of scripts/client/default.binds.cs we initiate gamePadGuiMap.

if ( isObject( gamePadGuiMap ) )
   gamePadGuiMap.delete();
new ActionMap(gamePadGuiMap);
 
function gamepadGuiUp(%val)
{
   if(%val)
   {
      updateLocalGamePadArrayUp();
   }
}
 
function gamePadGuiDown(%val)
{
   if(%val)
   {
      updateLocalGamePadArrayDown();
   }
}
 
function gamePadGuiSelect(%val)
{
   if(%val)
   {
      updateLocalGamePadArraySelect();
   }
}
 
gamePadGuiMap.bindCmd(gamepad, dpadu, "gamePadGuiUp(1);", "gamePadGuiDown(0);");
gamePadGuiMap.bindCmd(gamepad, dpadd, "gamePadGuiDown(1);", "gamePadGuiUp(0);");
gamePadGuiMap.bind(gamepad, btn_a, "gamePadGuiSelect");

 

We navigate using the D-Pad up/down and activate the button using A on the gamepad.


Here's what it looks like in action.

new_mm_padselect.png


And here's what it looks like when the mouse overrides it.

new_mm_mouseselect.png


So, as I said, hacky, but it does work - if you have one column. If anyone actually has a working example of "GuiGameListMenuCtrl" or a better way of doing this please post it somewhere. :idea:


And here's the full MainMenuGui.gui.

//--- OBJECT WRITE BEGIN ---
%guiContent = new GuiChunkedBitmapCtrl(MainMenuGui) {
   bitmap = "art/gui/background";
   useVariable = "0";
   tile = "0";
   position = "0 0";
   extent = "1024 768";
   minExtent = "8 8";
   horizSizing = "width";
   vertSizing = "height";
   profile = "GuiDefaultProfile";
   visible = "1";
   active = "1";
   tooltipProfile = "GuiToolTipProfile";
   hovertime = "1000";
   isContainer = "1";
   canSave = "1";
   canSaveDynamicFields = "1";
      enabled = "1";
      isDecoy = "0";
 
   new GuiBitmapCtrl(MainMenuAppLogo) {
      bitmap = "art/gui/Torque-3D-logo.png";
      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 GuiControl() {
      position = "359 157";
      extent = "306 454";
      minExtent = "8 2";
      horizSizing = "center";
      vertSizing = "center";
      profile = "GuiDefaultProfile";
      visible = "1";
      active = "1";
      tooltipProfile = "GuiToolTipProfile";
      hovertime = "1000";
      isContainer = "1";
      canSave = "1";
      canSaveDynamicFields = "0";
 
      new GuiButtonCtrl(MainMenuPlay) {
         text = "Play";
         groupNum = "-1";
         buttonType = "PushButton";
         useMouseEvents = "1";
         position = "9 18";
         extent = "289 75";
         minExtent = "8 8";
         horizSizing = "relative";
         vertSizing = "bottom";
         profile = "GuiMenuButtonProfile";
         visible = "1";
         active = "1";
         command = "Canvas.pushDialog(ChooseLevelDlg);";
         tooltipProfile = "GuiToolTipProfile";
         hovertime = "1000";
         isContainer = "1";
         canSave = "1";
         canSaveDynamicFields = "0";
 
         new GuiBitmapCtrl() {
            bitmap = "core/art/gui/images/dropslider_d.png";
            wrap = "0";
            position = "0 21";
            extent = "32 32";
            minExtent = "8 2";
            horizSizing = "right";
            vertSizing = "bottom";
            profile = "GuiDefaultProfile";
            visible = "1";
            active = "1";
            tooltipProfile = "GuiToolTipProfile";
            hovertime = "1000";
            isContainer = "0";
            canSave = "1";
            canSaveDynamicFields = "0";
         };
      };
      new GuiButtonCtrl(MainMenuJoin) {
         text = "Join";
         groupNum = "-1";
         buttonType = "PushButton";
         useMouseEvents = "1";
         position = "9 90";
         extent = "289 75";
         minExtent = "8 8";
         horizSizing = "relative";
         vertSizing = "bottom";
         profile = "GuiMenuButtonProfile";
         visible = "1";
         active = "1";
         command = "Canvas.pushDialog(JoinServerDlg);";
         tooltipProfile = "GuiToolTipProfile";
         hovertime = "1000";
         isContainer = "1";
         canSave = "1";
         canSaveDynamicFields = "0";
 
         new GuiBitmapCtrl() {
            bitmap = "core/art/gui/images/dropslider_d.png";
            wrap = "0";
            position = "0 21";
            extent = "32 32";
            minExtent = "8 2";
            horizSizing = "right";
            vertSizing = "bottom";
            profile = "GuiDefaultProfile";
            visible = "1";
            active = "1";
            tooltipProfile = "GuiToolTipProfile";
            hovertime = "1000";
            isContainer = "0";
            canSave = "1";
            canSaveDynamicFields = "0";
         };
      };
      new GuiButtonCtrl(MainMenuOptions) {
         text = "Options";
         groupNum = "-1";
         buttonType = "PushButton";
         useMouseEvents = "1";
         position = "9 162";
         extent = "289 75";
         minExtent = "8 8";
         horizSizing = "relative";
         vertSizing = "bottom";
         profile = "GuiMenuButtonProfile";
         visible = "1";
         active = "1";
         command = "Canvas.pushDialog(optionsDlg);";
         tooltipProfile = "GuiToolTipProfile";
         hovertime = "1000";
         isContainer = "1";
         canSave = "1";
         canSaveDynamicFields = "0";
 
         new GuiBitmapCtrl() {
            bitmap = "core/art/gui/images/dropslider_d.png";
            wrap = "0";
            position = "0 21";
            extent = "32 32";
            minExtent = "8 2";
            horizSizing = "right";
            vertSizing = "bottom";
            profile = "GuiDefaultProfile";
            visible = "1";
            active = "1";
            tooltipProfile = "GuiToolTipProfile";
            hovertime = "1000";
            isContainer = "0";
            canSave = "1";
            canSaveDynamicFields = "0";
         };
      };
      new GuiButtonCtrl(MainMenuGuiEditor) {
         text = "Gui Editor";
         groupNum = "-1";
         buttonType = "PushButton";
         useMouseEvents = "1";
         position = "9 234";
         extent = "289 75";
         minExtent = "8 8";
         horizSizing = "relative";
         vertSizing = "bottom";
         profile = "GuiMenuButtonProfile";
         visible = "1";
         active = "1";
         command = "GuiEdit();";
         tooltipProfile = "GuiToolTipProfile";
         tooltip = "The GUI Editor is accessible in-game by pressing F10";
         hovertime = "1000";
         isContainer = "1";
         internalName = "GuiEditorButton";
         canSave = "1";
         canSaveDynamicFields = "0";
 
         new GuiBitmapCtrl() {
            bitmap = "core/art/gui/images/dropslider_d.png";
            wrap = "0";
            position = "0 21";
            extent = "32 32";
            minExtent = "8 2";
            horizSizing = "right";
            vertSizing = "bottom";
            profile = "GuiDefaultProfile";
            visible = "1";
            active = "1";
            tooltipProfile = "GuiToolTipProfile";
            hovertime = "1000";
            isContainer = "0";
            canSave = "1";
            canSaveDynamicFields = "0";
         };
      };
      new GuiButtonCtrl(MainMenuWorldEditor) {
         text = "World Editor";
         groupNum = "-1";
         buttonType = "PushButton";
         useMouseEvents = "1";
         position = "9 306";
         extent = "289 75";
         minExtent = "8 8";
         horizSizing = "relative";
         vertSizing = "bottom";
         profile = "GuiMenuButtonProfile";
         visible = "1";
         active = "1";
         command = "toggleEditor(1);";
         tooltipProfile = "GuiToolTipProfile";
         tooltip = "The World Editor is accessible in-game by pressing F11";
         hovertime = "1000";
         isContainer = "1";
         internalName = "WorldEditorButton";
         canSave = "1";
         canSaveDynamicFields = "0";
 
         new GuiBitmapCtrl() {
            bitmap = "core/art/gui/images/dropslider_d.png";
            wrap = "0";
            position = "0 21";
            extent = "32 32";
            minExtent = "8 2";
            horizSizing = "right";
            vertSizing = "bottom";
            profile = "GuiDefaultProfile";
            visible = "1";
            active = "1";
            tooltipProfile = "GuiToolTipProfile";
            hovertime = "1000";
            isContainer = "0";
            canSave = "1";
            canSaveDynamicFields = "0";
         };
      };
      new GuiButtonCtrl(MainMenuExit) {
         text = "Exit";
         groupNum = "-1";
         buttonType = "PushButton";
         useMouseEvents = "1";
         position = "9 378";
         extent = "289 75";
         minExtent = "8 8";
         horizSizing = "relative";
         vertSizing = "bottom";
         profile = "GuiMenuButtonProfile";
         visible = "1";
         active = "1";
         command = "quit();";
         tooltipProfile = "GuiToolTipProfile";
         hovertime = "1000";
         isContainer = "1";
         internalName = "ExitButton";
         canSave = "1";
         canSaveDynamicFields = "0";
 
         new GuiBitmapCtrl() {
            bitmap = "core/art/gui/images/dropslider_d.png";
            wrap = "0";
            position = "0 21";
            extent = "32 32";
            minExtent = "8 2";
            horizSizing = "right";
            vertSizing = "bottom";
            profile = "GuiDefaultProfile";
            visible = "1";
            active = "1";
            tooltipProfile = "GuiToolTipProfile";
            hovertime = "1000";
            isContainer = "0";
            canSave = "1";
            canSaveDynamicFields = "0";
         };
      };
   };
};
//--- OBJECT WRITE END ---
Link to comment
Share on other sites

  • 2 weeks later...

Also getting rid of $activeGamePadGuiArray; and replacing it with:

 

//For Awake() guis
//...
   //Store as ebil globalist ...
   //$activeGamePadGuiArray = %list;//every new gamepad gui needs this setting for itself onWake
   %this.gamepadCtrl = %list;//begone ebil globalist
//...

 

//for functions
//...
function updateLocalGamePadArray(%num)
{
   //make global a local var
   //%list = $activeGamePadGuiArray;//out
   %list = getActiveGamePadCtrl();//in
 
   if(%list == 0)
      return;
//...
function updateLocalGamePadArrayUp()
{
   //get the currently active gamepad gui button array
   //%list = $activeGamePadGuiArray;//out
   %list = getActiveGamePadCtrl();//in
 
   if(%list == 0)
      return;
//...
function updateLocalGamePadArrayDown()
{
   //get the currently active gamepad gui button array
   //%list = $activeGamePadGuiArray;//out
   %list = getActiveGamePadCtrl();//in
 
   if(%list == 0)
      return;
//...
function updateLocalGamePadArraySelect()
{
   //get the currently active gamepad gui button array
   //%list = $activeGamePadGuiArray;//out
   %list = getActiveGamePadCtrl();//in
 
   if(%list == 0)
      return;

 

Which then accesses:

 

function getActiveGamePadCtrl()
{
   %num = Canvas.getCount();
   %cur = %num - 1;
   %gui = Canvas.getObject(%cur);
   %id = %gui.gamepadCtrl;
 
   if(%id)
      return %id;
   else
      return 0;
}
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...