Input with the ArcadeInputManager and the GamepadManager

The point of an xplat gaming framework like Proton is to be able to say “Move the character up, if they push up” and not care if they used a joystick, gamepad, on-screen directional pad, trackball, mouse, or bluetooth keyboard or whatever the heck they invent next.

The best way to do this in Proton is to have an ArcadeInputComponent somewhere and allow this to do most of the work. Example (Also, you can look at RTLooneyLadders for a working example):

#include "Entity/ArcadeInputComponent.h"

//somewhere in some init function...
ArcadeInputComponent *pComp = (ArcadeInputComponent*) GetEntityRoot()->AddComponent(new ArcadeInputComponent); //make it somewhere
GetBaseApp()->m_sig_arcade_input.connect(&OnArcadeInput);  //connect it to a function to receive input from

//Add some mappings so so the m_sig_arcade_input will get hit with it:
AddKeyBinding(pComp, "Left", VIRTUAL_KEY_DIR_LEFT, VIRTUAL_KEY_DIR_LEFT);
....
	

void OnArcadeInput(VariantList *pVList)
{

	int vKey = pVList->Get(0).GetUINT32();
	bool bIsDown = pVList->Get(1).GetUINT32() != 0;

	LogMsg("GameMenuArcade: Key %d, down is %d", vKey, int(bIsDown));

	switch (vKey)
	{
		case VIRTUAL_KEY_DIR_LEFT:
                LogMsg("We got left!");
			break;
	}

}

Ok, so now pressing the LEFT arrow will show “We got left!”. Pressing the left direction on an Xperia pad will also trigger it, no special setup is required for that. But what if you also went to let people use a touch control to handle left? Easy, just have your button emit a virtual key:

Entity *pEnt = CreateOverlayButtonEntity(pBG, "left", "left_button_image.rttex", 100, 100);
MakeButtonEmitVirtualGameKey(pEnt, VIRTUAL_KEY_DIR_LEFT);

Easy enough. Now this touch button will send fake virtual press/release signals through the ArcadeInputManager, so your game logic doesn't have to care where the button press came from.

The ArcadeInputManager has special code when it notices a directional key is being used and offers ways to grab info on the current direction directly with these functions:

bool GetDirectionKeysAsVector(CL_Vec2f *pVecOut);
bool GetDirectionKeys(bool &bLeftOut, bool &bRightOut, bool &bUpOut, bool &bDownOut);

Ok, what if you want to connect a 360 controller via directX or an iCade controller on your iPad? The GamepadManager allows you to connect to one or many gamepads and offer multiple ways of getting data:

Notes:

Adding the GamepadManager to an existing project

Ok, let's assume you're using an ArcadeInputManager for your input and you want to add gamepad support. Here is how to add it.

Add this to your App.cpp:

//put this include near the top
#include "Gamepad/GamepadManager.h"

//add this under that
GamepadManager g_gamepadManager;
GamepadManager * GetGamepadManager() {return &g_gamepadManager;}

In App::Update(), add the following under BaseApp::Update() somewhere:

g_gamepadManager.Update();

That's it. Now, you just need to add some GamepadProvider's to the GamepadManager and you're good to go.

Here is a quick example, this would add support for Directx gamepads on Windows, and iCade sticks on everything else:

//near the top of App.cpp, add these somewhere:
#ifdef PLATFORM_WINDOWS
    #include "Gamepad/GamepadProviderDirectX.h"
#endif
#include "Gamepad/GamepadProvideriCade.h"


//in your App::Init() do this somewhere:

#ifdef PLATFORM_WINDOWS
	GetGamepadManager()->AddProvider(new GamepadProviderDirectX); //use directx joysticks
#endif

GetGamepadManager()->AddProvider(new GamepadProvideriCade); //use iCade, this actually should work with any platform...

Next, to actually use the pads, you must connect them to the ArcadeInputComponent. Add this right after you init your ArcadeInputComponent:


//ArcadeInputComponent *pComp = (ArcadeInputComponent*)pIcon->AddComponent(new ArcadeInputComponent);

//Just connect to all pads at once.. ok to do for a single player game.. otherwise, we should use
//Gamepad *pPad = GetGamepadManager()->GetDefaultGamepad(); or GetUnusedGamepad() instead probably, or let the user choose.
//Keep in mind pads can be removed/added on the fly


	for (int i=0; i < GetGamepadManager()->GetGamepadCount(); i++)
	{
		Gamepad *pPad = GetGamepadManager()->GetGamepad((eGamepadID)i);
		pPad->ConnectToArcadeComponent(pComp, true, true);

		//if we cared about the analog sticks too, we'd do this:
		//pPad->m_sig_left_stick.connect(1, boost::bind(&OnGamepadStickUpdate, this, _1));	
		//pPad->m_sig_right_stick.connect(1, boost::bind(&OnGamepadStickUpdate, this, _1));	
	}

That's it, now your game can be controlled by the iCade/360 pad without changing your game-specific input code at all.

To map to specific joystick buttons, you can use the following virtual keys with the ArcadeInputComponent:

	VIRTUAL_DPAD_BUTTON_LEFT, 
	VIRTUAL_DPAD_BUTTON_UP, 
	VIRTUAL_DPAD_BUTTON_RIGHT, //generally considered a select button
	VIRTUAL_DPAD_BUTTON_DOWN, //generally considered a back button - map to VIRTUAL_KEY_DIR_CENTER instead for xperia play
	VIRTUAL_DPAD_SELECT,
	VIRTUAL_DPAD_START,
	VIRTUAL_DPAD_LBUTTON,
	VIRTUAL_DPAD_RBUTTON,
	VIRTUAL_DPAD_LTRIGGER,
	VIRTUAL_DPAD_RTRIGGER,
	VIRTUAL_DPAD_HAT_UP, //a hat is like the DPAD thingie on a 360 controller
	VIRTUAL_DPAD_HAT_RIGHT,
	VIRTUAL_DPAD_HAT_DOWN,
	VIRTUAL_DPAD_HAT_LEFT

Note, the VIRTUAL_DPAD_HAT_* are also handled as directional keys on a 360 controller, so the player can use the stick or the pad it.

Also, you'd have to add dinput8.lib and dxguid.lib to your “Additional dependencies” to get this to actually work with the directx part.

Conclusion

For specific instructions on setting up each kind of controller, visit the main proton page.