Game Development Community

dev|Pro Game Development Curriculum

Easier Control Maping System

by Kevin Mitchell · 04/01/2014 (9:40 am) · 31 comments

This is a system that uses the packaging functions built into the engine. I personally find this easier vs creating action maps over and over and helps for re-skinning mapped button controls to other actions and systems.


With this tutorial I will cover mapping the controller menu controls. And switching to world mappings.

First Generic Mapping:

The mapping is general and should never change. This will help control pushing and popping and not really knowing what mapped is pushed at a given time.


//-----------------------------------------------------------------------------
// Torque
// Copyright GarageGames, LLC 2011
//-----------------------------------------------------------------------------
echo("------------ ( RPG Move Map Activated )----------------------------------------------");
if ( isObject( rpgMoveMap ) )
   rpgMoveMap.delete();
new ActionMap(rpgMoveMap);


//CONTROL WITH CONTROLER
rpgMoveMap.bind(gamepad, dpadl,                          RPG_L_BTN);
rpgMoveMap.bind(gamepad, dpadu,                          RPG_U_BTN);
rpgMoveMap.bind(gamepad, dpadd,                          RPG_D_BTN);
rpgMoveMap.bind(gamepad, dpadr,                          RPG_R_BTN);

rpgMoveMap.bind( gamepad, thumbrx, "D", "-0.05 0.05",    RPG_YAW_X );
rpgMoveMap.bind( gamepad, thumbry, "D", "-0.05 0.05",    RPG_PITCH_Y );
rpgMoveMap.bind( gamepad, thumblx, "D", "-0.05 0.05",    RPG_MOVE_X );
rpgMoveMap.bind( gamepad, thumbly, "D", "-0.05 0.05",    RPG_MOVE_Y );

rpgMoveMap.bind( gamepad, btn_l,                         RPG_L1_Trigger);
rpgMoveMap.bind( gamepad, btn_r,                         RPG_R1_Trigger);

rpgMoveMap.bind( gamepad, triggerl,                      RPG_L2_Trigger);
rpgMoveMap.bind( gamepad, triggerr,                      RPG_R2_Trigger);

rpgMoveMap.bind( gamepad, btn_lt,                        RPG_L3_Trigger);
rpgMoveMap.bind( gamepad, btn_rt,                        RPG_R3_Trigger);

rpgMoveMap.bind( gamepad, btn_back,                      RPG_Select );
rpgMoveMap.bind( gamepad, btn_start,                     RPG_Start );

rpgMoveMap.bind( gamepad, btn_a,                         RPG_A_BTN );
rpgMoveMap.bind( gamepad, btn_b,                         RPG_B_BTN );
rpgMoveMap.bind( gamepad, btn_x,                         RPG_X_BTN );
rpgMoveMap.bind( gamepad, btn_y,                         RPG_Y_BTN );


//CONTROL WITH KEYBOARD
rpgMoveMap.bind( keyboard, left, RPG_L_BTN );
rpgMoveMap.bind( keyboard, right, RPG_R_BTN );
rpgMoveMap.bind( keyboard, up, RPG_U_BTN );
rpgMoveMap.bind( keyboard, down, RPG_D_BTN );

rpgMoveMap.bind( keyboard, a, moveleft );
rpgMoveMap.bind( keyboard, d, moveright );
rpgMoveMap.bind( keyboard, w, moveforward );
rpgMoveMap.bind( keyboard, s, movebackward );

rpgMoveMap.bind( mouse, xaxis, yaw );
rpgMoveMap.bind( mouse, yaxis, pitch );
rpgMoveMap.bind( keyboard, space,                   RPG_X_BTN );

rpgMoveMap.bind( keyboard, q,                       RPG_L1_Trigger);//Quick Bar
rpgMoveMap.bind( keyboard, f,                       RPG_R1_Trigger);//Lock On

rpgMoveMap.bind( keyboard, lshift,                  RPG_L2_Trigger);//Sprint Trigger NOT WORKING
rpgMoveMap.bind( keyboard, e,                       RPG_R2_Trigger);//unused

rpgMoveMap.bind( keyboard, c,                       RPG_L3_Trigger);//Change Player
rpgMoveMap.bind( keyboard, tab,                     RPG_R3_Trigger);//Change Camera

rpgMoveMap.bind( keyboard, escape,                    RPG_Start );
rpgMoveMap.bind( keyboard, enter,                     RPG_A_BTN );//Attack Select
rpgMoveMap.bind( keyboard, backspace,                 RPG_B_BTN );//Cancel

rpgMoveMap.bind( mouse, button0, RPG_A_BTN );//Cancel
rpgMoveMap.bind( mouse, button1, RPG_B_BTN);//Attack
rpgMoveMap.bind( mouse, button2, RPG_Y_BTN );//Special



//Mapping Package - Title Screen

This is a general mapping skinned for the title screen. There are some un used functions but during the course for development you may find use for said mappings.


//KeyMapTitleScreen.cs
package KeyMapTitleScreen
{

function RPG_U_BTN(%val)
{
   if(%val)MainMenuGui::Up();
}

function RPG_D_BTN(%val)
{
   if(%val)MainMenuGui::Down();
}

function RPG_L_BTN(%val){}
function RPG_R_BTN(%val){}
function RPG_YAW_X(%val){}
function RPG_PITCH_Y(%val){}
function RPG_MOVE_X(%val){}
function RPG_MOVE_Y(%val){}

function RPG_A_BTN(%val)
{
   if(%val)MainMenuGui::Select();
}

//jump function
function RPG_B_BTN(%val){}
function RPG_X_BTN(%val){}
function RPG_Y_BTN(%val){}
function RPG_L1_Trigger(%val){}
function RPG_R1_Trigger(%val){}
function RPG_L2_Trigger(%val){}
function RPG_R2_Trigger(%val){}
function RPG_L3_Trigger(%val){}
function RPG_R3_Trigger(%val){}
function RPG_Select(%val){}

function RPG_Start(%val){
   if(%val && $IN_WORLD_EDITOR)toggleEditor( true );
}

//System Fucntions
function gamePadMoveX( %val ){}
function gamePadMoveY( %val ){}
function gamepadYaw(%val){}
function gamepadPitch(%val){}

//KEYBOARD INJECTION
function moveleft(%val){}
function moveright(%val){}
function moveforward(%val){}
function movebackward(%val){}
function moveup(%val){}
function movedown(%val){}
function turnLeft( %val ){}
function turnRight( %val ){}
function panUp( %val ){}
function panDown( %val ){}
};//package




Next is the mapping system that simply switches you from on bindings to another. This can be used at the wake and sleep of what every menu we are controlling, or after a given interaction like mounting a vehicular.

//MappingSystem.cs
//Used when all input needs to be disabled
$KEY::NullMode = 0;
//
$KEY::TitleMode = 1;
$KEY::RPGMode = 2;
$KEY::PauseMode = 3;
$KEY::GOMode = 4;
$KEY::QBARMode = 5;
$KEY::EventNavigation = 6;
$KEY::KeyMapItemStoreOptions = 7;
$KEY::KeyMapItemStoreSelectList = 8;
$KEY::KeyMapItemStoreTotal = 9;
$KEY::KeyMapItemStoreSellSelectList = 10;
$KEY::KeyMapItemStoreSellTotal = 11;
$KEY::KeyMapSaveGame = 12;
$KEY::KeyMapInnStoreOptions = 13;
$KEY::CutSceenControls = 14;
$KEY::CarControls = 15;
$KEY::KeyMapBattleMode = 16;
$KEY::KeyMapWorldEDTMode = 17;


$activeKeyPackage = $KEY::TitleMode;

 function changeToPackage(%newOne)
{
	$lastPackage = $activeKeyPackage;
	$activeKeyPackage = %newOne;
	switch($lastPackage)
	{
	   
	      case $KEY::NullMode:
		 deactivatePackage(KeyMapNull);
		 rpgMoveMap.pop();
	      case $KEY::TitleMode:
		 deactivatePackage(KeyMapTitleScreen);
		 rpgMoveMap.pop();
	      case $KEY::RPGMode:
		 deactivatePackage(KeyMapRPGMode);
		 rpgMoveMap.pop();
	      case $KEY::PauseMode:
		 deactivatePackage(KeyMapPauseMenuMain); 
		 rpgMoveMap.pop();
	      case $KEY::KeyMapWorldEDTMode:
		 deactivatePackage(KeyMapWorldEDTMode); 
		 rpgMoveMap.pop();

	      default :
		 error("------INVALID MAP SENT----------");
         
	}
	
	switch($activeKeyPackage)
	{
	      case $KEY::NullMode:
		 activatePackage(KeyMapNull);
		 rpgMoveMap.push();
	      case $KEY::TitleMode:
		 activatePackage(KeyMapTitleScreen);
		 rpgMoveMap.push();
	      case $KEY::RPGMode:
		 activatePackage(KeyMapRPGMode);
		 rpgMoveMap.push();
	      case $KEY::PauseMode:
		 activatePackage(KeyMapPauseMenuMain); 
		 rpgMoveMap.push();
	      case $KEY::KeyMapWorldEDTMode:
		 activatePackage(KeyMapWorldEDTMode); 
		 rpgMoveMap.push();

	      default :
		 error("------INVALID MAP SENT----------");

	}
}


This is an example of my title menu and how i use the mapping to control shifting through the Menu GUI.

//mainMenuGui.cs
$TITLE::INDEX=0;
//------------------------------------------------------------------------------
function MainMenuGui::onAdd(%this)
{
   $TITLE::INDEX=0;
   
}

//------------------------------------------------------------------------------
function MainMenuGui::onWake(%this)
{
   // Play sound...
   $enableDirectInput = "1";
   activateDirectInput();
   enableJoystick();
   TITLE_2DIMAGE.visible=(!$LEVELTYPE::USING_3D_MENU);
   //playEnvMusic("TitleScreenTheme.ogg",3);
   changeToPackage($KEY::TitleMode);   
}

//------------------------------------------------------------------------------
function MainMenuGui::onSleep(%this)
{
   // Clear the load info:
   // Stop sound...
   changeToPackage($KEY::NullMode);
   playEnvMusic("",0);
   TITLE_LIST.clear();
}

function TITLE_LIST::onWake(%this)
{
   if(TITLE_LIST.getRowCount()==0){
      %this.addRow("NEW GAME", "startNewGame", 0);
      %this.addRow("LOAD GAME", "openLoadMenu", 2, -15);
      %this.addRow("OPTIONS", "openOptionsMenu", 4, -15);
      %this.addRow("EXIT GAME", "quit", 6, -15);
   }
}

function MainMenuGui::Up(){
   $TITLE::INDEX--;
   if($TITLE::INDEX<0)$TITLE::INDEX=2;
   TITLE_LIST.setSelected($TITLE::INDEX);
}
function MainMenuGui::Down(){
   $TITLE::INDEX++;
   if($TITLE::INDEX>=3)$TITLE::INDEX=0;
   TITLE_LIST.setSelected($TITLE::INDEX);
}
function openOptionsMenu(){
   Canvas.pushDialog(RPGoptionsDlg);
}

function MainMenuGui::Select(){
   TITLE_LIST.activateRow();
}



And if you want to see the main game mappings mine looks like this. This is activated when ever the play GUI is reactivated mind you one thing that I did change was to track the last Game play mappings to make sure that I returned to the Vehicle mapping or the Player mapping but this can easily be stored when leaving the playGui.

function SavePlayPackage(){
$PlayGuiParentPackage=$activeKeyPackage;
changeToPackage($KEY::$KEY::PauseMode);
}

function RestorePlayPackage(){
   if($PlayGuiParentPackage>$KEY::NullMode){
        changeToPackage($PlayGuiParentPackage);
   }else{
        changeToPackage($KEY::RPGMode);
   }
}


Game Play Mapping i use for game play, this should have all controller and keyboard functionalities tied together in one package.


package KeyMapRPGMode
{

function RPG_U_BTN(%val)
{
   if(%val)toggleHover(0);
}

function RPG_D_BTN(%val){}
function RPG_L_BTN(%val){}
function RPG_R_BTN(%val){}
function RPG_YAW_X(%val)
{
   gamepadYaw(%val);
}

function RPG_PITCH_Y(%val)
{
   gamepadPitch(%val);
}

function RPG_MOVE_X(%val)
{
   gamePadMoveX(%val);
}

function RPG_MOVE_Y(%val)
{
   gamePadMoveY(%val);
}

function RPG_A_BTN(%val)
{
   if(%val){
      switch$($PLAYER::PlayerInteractionCheck){
         case $Interaction::ItemStore :
            Canvas.setContent(ItemStore);
         case $Interaction::ArmorStore :
         case $Interaction::WeaponStore :
         case $Interaction::NPC :
            if(isObject($PLAYER::InteractNPC) && $PlayerObject::ScanEnabled==true){
               PL_Interaction_IMG.visible=false;
               StopScan();
               $PLAYER::InteractNPC.PROCESS_EVENTS();
               $PLAYER::InteractNPC.PrevState = $PLAYER::InteractNPC.CurrentState;
               $PLAYER::InteractNPC.CurrentState = $NPC::Idle;
               $PLAYER::InteractNPC=0;
            }
         case $Interaction::TreasureChest :
         case $Interaction::BattleGrounds :
      }
   }
}


function RPG_B_BTN(%val){}

//jump function
function RPG_X_BTN(%val)
{
   $mvTriggerCount2++;
}

function RPG_Y_BTN(%val){}
function RPG_L1_Trigger(%val){}
function RPG_R1_Trigger(%val){}

//Custom Trigger
function RPG_L2_Trigger(%val){
   if(%val)$mvTriggerCount5++;
}
function RPG_R2_Trigger(%val){
   if(%val)$mvTriggerCount1++;
}
function RPG_L3_Trigger(%val){
   switchPlayer(%val);
}
function RPG_R3_Trigger(%val){
   toggleFirstPerson(%val);
}

function RPG_Select(%val){}

function RPG_Start(%val){
   if(%val && $IN_WORLD_EDITOR){
      toggleEditor( true );
   }else{
      if(%val){
         $PAUSING_GAME=true;
         Canvas.setContent(PauseMenu);
      }
   }
}


//System Fucntions
function gamePadMoveX( %val )
{
   if(%val){
      $RPG_PLAYER_MOVING=1;
   }else{
      $RPG_PLAYER_MOVING=0;
   }
   %MoveVel = getMoveAdjustAmount(%val);
   commandToServer('setAttackingX',%MoveVel);
   //if($LOCK::ON)%MoveVel/=2;
   if(%MoveVel > 0)
   {
      $mvRightAction = %MoveVel;
      $mvLeftAction = 0;
   }
   else
   {
      $mvRightAction = 0;
      $mvLeftAction = -%MoveVel;
   }
}

function gamePadMoveY( %val )
{
   if(%val){
      $RPG_PLAYER_MOVING=1;
   }else{
      $RPG_PLAYER_MOVING=0;
   }
   %MoveVel = getMoveAdjustAmount(%val);
   commandToServer('setAttackingY',%MoveVel);
   if(%MoveVel > 0)
   {
      $mvForwardAction = %MoveVel;
      $mvBackwardAction = 0;
   }
   else
   {
      $mvForwardAction = 0;
      $mvBackwardAction = -%MoveVel;
   }
}

function gamepadYaw(%val)
{
   if($LOCK::ON)return;
   %yawAdj = getGamepadAdjustAmount(%val);
   if(ServerConnection.isControlObjectRotDampedCamera())
   {
      // Clamp and scale
      %yawAdj = mClamp(%yawAdj, -m2Pi()+0.01, m2Pi()-0.01);
      %yawAdj *= 0.5;
   }

   if(%yawAdj > 0)
   {
      $mvYawLeftSpeed = %yawAdj;
      $mvYawRightSpeed = 0;
   }
   else
   {
      $mvYawLeftSpeed = 0;
      $mvYawRightSpeed = -%yawAdj;
   }
}

function gamepadPitch(%val)
{
   %pitchAdj = getGamepadAdjustAmount(%val);
   if(ServerConnection.isControlObjectRotDampedCamera())
   {
      // Clamp and scale
      %pitchAdj = mClamp(%pitchAdj, -m2Pi()+0.01, m2Pi()-0.01);
      %pitchAdj *= 0.5;
   }

   if(%pitchAdj > 0)
   {
      $mvPitchDownSpeed = %pitchAdj;
      $mvPitchUpSpeed = 0;
   }
   else
   {
      $mvPitchDownSpeed = 0;
      $mvPitchUpSpeed = -%pitchAdj;
   }
}
//KEYBOARD INJECTION
function moveleft(%val)
{
   if(%val){
      $RPG_PLAYER_MOVING=1;
   }else{
      $RPG_PLAYER_MOVING=0;
   }
   commandToServer('setAttackingX',-1);
   $mvLeftAction = %val * $movementSpeed;
}

function moveright(%val)
{
   if(%val){
      $RPG_PLAYER_MOVING=1;
   }else{
      $RPG_PLAYER_MOVING=0;
   }
   commandToServer('setAttackingX',1);
   $mvRightAction = %val * $movementSpeed;
}

function moveforward(%val)
{
   if(%val){
      $RPG_PLAYER_MOVING=1;
   }else{
      $RPG_PLAYER_MOVING=0;
   }
   commandToServer('setAttackingY',1);
   $mvForwardAction = %val * $movementSpeed;
}

function movebackward(%val)
{
   if(%val){
      $RPG_PLAYER_MOVING=1;
   }else{
      $RPG_PLAYER_MOVING=0;
   }
   commandToServer('setAttackingY',-1);
   $mvBackwardAction = %val * $movementSpeed;
}

function moveup(%val)
{
   %object = ServerConnection.getControlObject();
   
   if(%object.isInNamespaceHierarchy("Camera"))
      $mvUpAction = %val * $movementSpeed;
}

function movedown(%val)
{
   %object = ServerConnection.getControlObject();
   
   if(%object.isInNamespaceHierarchy("Camera"))
      $mvDownAction = %val * $movementSpeed;
}

function turnLeft( %val )
{
   $mvYawRightSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}

function turnRight( %val )
{
   $mvYawLeftSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}

function panUp( %val )
{
   $mvPitchDownSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}

function panDown( %val )
{
   $mvPitchUpSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}
};//package




The idea is to add a new "Package" Instead of adding a new bindings. This way if your layout change you change one thing and not 5000.


Hope this helps.


Page «Previous 1 2
#1
04/01/2014 (2:13 pm)
Thanks for posting this. I will test it out and get back to you.
#2
04/01/2014 (2:42 pm)
It works somewhat. I can move up and down the menu with the controller but I can't press a button to activate the button.
#3
04/01/2014 (5:27 pm)
Check you mapping. add a trace in the button mappings
#4
04/01/2014 (5:28 pm)
I had these in my code because my mapping for the buttons were not what i though at first.

function RPG_A_BTN(%val)
{
   if($_DEBUG_BTNS&&%val)echo("RPG_A_BTN");
   if(%val)MainMenuGui::Select();
}

//jump function
function RPG_B_BTN(%val)
{
   if($_DEBUG_BTNS&&%val)echo("RPG_B_BTN");
   
}

function RPG_X_BTN(%val)
{
   if($_DEBUG_BTNS&&%val)echo("RPG_X_BTN");
}

function RPG_Y_BTN(%val)
{
   if($_DEBUG_BTNS&&%val)echo("RPG_Y_BTN");
}
#5
04/01/2014 (10:20 pm)
That did the trick. Now I'll have to make a menus. Thanks for the help. I'll let you know if I run into any problems.
#6
04/02/2014 (4:35 am)
I have a question. Here's an example.

youtu.be/Wxr-gJsTJFE

Forgot how to post a preview window.

So with Option the player selected Video Options and it bring up that screen. How do I move the control to the Video Options to change the resolution and then move the control back to the menu? Now of course this example is using a mouse but the controller would work the same.
#7
04/02/2014 (7:00 am)
This would be where you have multi maps one for each section of the gui. after the selection of an option the side gui opens and u switch the package that controls the new side panel.
#8
04/02/2014 (7:01 am)
Would it be possible for you to create an example?
#9
04/02/2014 (8:31 am)
Look on my old resources for rpg store it's old and clunky but it works there's a newer version in my rpg engine
#10
04/02/2014 (8:35 am)
Is this the resource you are talking about? RPG Resource 4 AKA Invertory/Store
I noticed it's using sqlite. I'm guessing I'll have to change all it?
#11
04/02/2014 (9:06 am)
yes you can bring your data in how ever you want i just find sql faster and more dynamic
#12
04/29/2014 (4:18 pm)
I have been trying to figure out how to move the control from the side menu to another part in the menu and back. Just like in the video, youtu.be/Wxr-gJsTJFE.
#13
04/29/2014 (6:01 pm)
Using the mouse or a controller?
#14
04/29/2014 (6:48 pm)
Using the controller.
#15
04/30/2014 (1:17 pm)
Anytime you select a button you should be setting a panel visible and when u hit back u should hide a panel

All you have to do is setup all your panels in one GUI and hide them all the left panel will enable the visibility of a given panel. When that panel is visible change the controls to another control set to control the contents of the new window
#16
06/17/2014 (2:25 am)
I hate to bother you Kevin but I have been struggling trying to get this working. I'm trying to do what you said by hiding panels but it's not working on very well for me. Any chance you could create a example menu? Thanks
#17
06/17/2014 (2:49 am)
https://mega.co.nz/#!clhGSZpB!57UNWhtXr4FzHmBEGzqzyOOxToUUKIkg9DgOpeo1xCw
#18
06/17/2014 (4:12 am)
Thanks, I'll give it a try.
#19
06/30/2014 (6:31 pm)
I have added your example files to my project. I have "exec"ed all the scripts and gui. When I launch the game I'm getting this error in the console.
art/gui/StoreMenu/ItemStore.cs (27): Unable to find function changeToPackage
#20
07/01/2014 (12:33 am)
If you have MappingSystem.cs from the code above you may should not have that issue.

The script files have a execution order that has to be fulfilled so if you execute a CS file that needs a function from a different cs file then you need to switch the order that you execute those files.
Page «Previous 1 2