GUI Navigation via joystick/gamepad/keyboard
by Chuck Nicholson · in Torque Game Engine · 08/28/2004 (12:33 pm) · 12 replies
Hello. I have been searching far and wide (resources, forums, google, documentation, textbooks) for something that will show me how to navigate through a menu system without using the mouse.
Similar to a console, I want to be able to use the joystick/gamepad to scroll through the menu choices while still using the highlight/depressed/normal/etc functionality.
So far all I see is mouse support. Anyone have any thoughts on this?
I was thinking I could add to or create my own button class or add a single value to the button Im using, call it ButtonNum. When I press the joystick POV up or down, or move the joystick up/down, there is a value which gets incremented/decremented, call it ButtonSelected. When ButtonSelected == ButtonNum, then that button is "selected". I'm thinking I could name each instance of a button and set all the button's values to false (mDepressed, mMouseOver, etc) and then loop throgh each of my buttons and if that's the selected button, change its method variable to true (mMouseOver);
Is there an easier way to do this? Has anyone implemented it? Although I was able to bind eys specifically while within the main menu, I cannot seem to change the button values. Is there a way to use the scope operator to access them from within a MainMenu function(Assume I named my button instances, which I did). I cant seem to get it to work yet.
Similar to a console, I want to be able to use the joystick/gamepad to scroll through the menu choices while still using the highlight/depressed/normal/etc functionality.
So far all I see is mouse support. Anyone have any thoughts on this?
I was thinking I could add to or create my own button class or add a single value to the button Im using, call it ButtonNum. When I press the joystick POV up or down, or move the joystick up/down, there is a value which gets incremented/decremented, call it ButtonSelected. When ButtonSelected == ButtonNum, then that button is "selected". I'm thinking I could name each instance of a button and set all the button's values to false (mDepressed, mMouseOver, etc) and then loop throgh each of my buttons and if that's the selected button, change its method variable to true (mMouseOver);
Is there an easier way to do this? Has anyone implemented it? Although I was able to bind eys specifically while within the main menu, I cannot seem to change the button values. Is there a way to use the scope operator to access them from within a MainMenu function(Assume I named my button instances, which I did). I cant seem to get it to work yet.
#2
If you need specifics, you can probably ask him nicely at patw@garagegames.com and he'll give you some more detailed explanation.
09/04/2004 (11:31 am)
We had to solve this problem for the Marble Blast X-box port. I believe that Pat used (abused? :P) the tab ordering functionality in the GUI controls, then bound the up/down buttons on the x-box controller to the tab next and tab previous commands. Then he set up a command that would "press" the currently selected control.If you need specifics, you can probably ask him nicely at patw@garagegames.com and he'll give you some more detailed explanation.
#3
04/20/2005 (3:44 pm)
I'm trying to do the same thing now. What Ben describes seems like a logical way to quickly hack this in, but i'd like to hear what Pat thinks now that he's had to live with it. =)
#4
04/20/2005 (4:05 pm)
I too am stuck with the same dilemma, though what Ben says does sound pretty simplistic and quick.
#5
Then what you do is, in script, add things to the GUI code, so say you have a GUI named mainMenuGui, you add something like:
04/20/2005 (4:12 pm)
I changed code in GUI Canvas input processing. The function is: GuiCanvas::processInputEvent, and this is what I addedelse if( event->deviceType == JoystickDeviceType &&
( XBLIVE->getLockedControler() == event->deviceInst || !XBLIVE->controlerLocked() ) ) // Added for XBox -pw
{
// Do NOT PUT THE gLastEventTime change here, it will pick up the analog stick noise
//copy the modifier into the new event
mLastEvent.modifier = event->modifier;
GuiControl *ctrl = static_cast<GuiControl*>( last() ); // Send events to the top level GUI
const char *retval = "";
if( event->objType == SI_BUTTON )
{
// TODO: Add a 'back' feature for XBox -pw
if( event->action == SI_MAKE )
{
if( Con::getBoolVariable( "$playingDemo" ) && !Con::getBoolVariable( "$gamePaused" ) )
{
Con::evaluate( "onDemoPlayDone(true);" );
return true;
}
switch( event->objInst )
{
case KEY_BUTTON0:
gLastButtonPressControlerInst = event->deviceInst;
retval = Con::executef( ctrl, 1, "onA" );
break;
// ... etc, etc
default:
return false;
}
}
// This block is duplicated because we don't want
// this being called if there wasn't a valid action (IE analog noise)
gLastEventTime = Sim::getCurrentTime();
if( dStrcmp( retval, "" ) == 0 )
retval = Con::executef( ctrl, 1, "onAction" );
if( dStrcmp( retval, "" ) )
return true;
}
else if( event->objType == SI_POV )
{
if( event->action == SI_MAKE )
{
switch( event->objInst )
{
case SI_UPOV:
retval = Con::executef( ctrl, 1, "onUp" );
break;
case SI_LPOV:
retval = Con::executef( ctrl, 1, "onLeft" );
break;
case SI_DPOV:
retval = Con::executef( ctrl, 1, "onDown" );
break;
case SI_RPOV:
retval = Con::executef( ctrl, 1, "onRight" );
break;
default:
return false;
}
}
gLastEventTime = Sim::getCurrentTime();
if( dStrcmp( retval, "" ) == 0 )
retval = Con::executef( ctrl, 1, "onAction" );
if( dStrcmp( retval, "" ) )
return true;
}
}
// .. Rest of function, mine ends here
return false;
}Then what you do is, in script, add things to the GUI code, so say you have a GUI named mainMenuGui, you add something like:
function MainMenuGui::onRight( %this )
{
// Do something here
return true;
}The "return true;" is important because, as you can see in the code, it looks for that return value.
#6
FYI. We have two main reasons we've chosen to implement gamepad controls in our prototype. First the genre of game we're doing is best played on a gamepad/joystick. Second adding mouse/keyboard controls to a game designed around a gamepad is much easier than the other way around.
04/20/2005 (6:11 pm)
@Pat - You rock man. Thanks a ton.FYI. We have two main reasons we've chosen to implement gamepad controls in our prototype. First the genre of game we're doing is best played on a gamepad/joystick. Second adding mouse/keyboard controls to a game designed around a gamepad is much easier than the other way around.
#7
To fix this i added the following to client\init.cs in the function initClient() near the bottom before the game and/or splash screens are started:
Now the joystick/gamepad are enabled when your game starts up and you can use them to skip the splash screens and navigate the main menu.
04/20/2005 (10:21 pm)
Ok something was missing from this (Pat correct me if i'm wrong here). In Starter.FPS direct input is not initialized until PlayGui::onWake()... which is way late if you want gamepad/joystick navigation of your main menu.To fix this i added the following to client\init.cs in the function initClient() near the bottom before the game and/or splash screens are started:
// Enabled direct input here so we can use // the joystick/gamepad for UI navigation. $enableDirectInput = "1"; activateDirectInput();
Now the joystick/gamepad are enabled when your game starts up and you can use them to skip the splash screens and navigate the main menu.
#8
I'm thinking about successively looking up the gui list until one that can handle it is found. Another idea is to skip over any gui element with a high layer value ( the fps metrics has a layer of 1000 and the console has a layer of 99 ). Any better ideas?
05/07/2005 (6:04 pm)
One little bump i've run into with the changes in GuiCanvas. It uses last() to decide what control to execute the script member functions which tell the control what was pressed. Well if the console is up or even the FPS window ( type "metrics( fps );" in the console ) then the gui which actually handles the gamepad input will not be called.I'm thinking about successively looking up the gui list until one that can handle it is found. Another idea is to skip over any gui element with a high layer value ( the fps metrics has a layer of 1000 and the console has a layer of 99 ). Any better ideas?
#9
05/07/2005 (8:29 pm)
What i ended up doing is looping from the end() gui entry to the begin()ing calling the script callback until either one returns true or i run out of controls. This lets each control on the canvas list a chance an handling the joystick/gamepad event.
#10
05/07/2005 (9:20 pm)
Ah, that is a good solution. My version was, pretty hacked. You are right about the whole enabling direct input thing. I didn't use DInput for the Xbox version.
#11
04/09/2006 (2:35 pm)
Does the code Pat Wilson posted work with 1.4? I can't get it to work.
Torque Owner Chuck Nicholson