Advertisement

Remapping Input for GameControllers; a Few Design Questions

Started by July 16, 2018 02:59 PM
8 comments, last by adt7 6 years, 1 month ago

So, I am currently redesigning my input system to more easily allow for input mapping. In terms of mouse and keyboard, no issues there. However, I seem to be having a bit of trouble wrapping my head around what to do for controllers.

I use SDL2 to read input events. SDL2 has 2 similar yet different APIs for controller input; the Joystick API and the GameController API. The Joystick API is very generic; it basically tells you how many buttons and axes the controller has, and leaves the rest for you to figure out. The GameController API is built on top of this system, but tries to map everything in a fashion similar to an XBox 360 controller, meaning that axes and buttons are fairly standardized, but this is only if the controller exists in SDL's database of known game controllers.

I want to support as many control options as possible, while maximizing convenience for the players (and myself). Keeping that in mind, here are the questions I have regarding the design of the system:

  1. Should I actually bother using the Joystick API, or should I only accept GameControllers? The GameController API being standardized is certainly a strong positive, but this fundamentally limits the player's control options. As far as I understand the GameController API, it is strictly limited to mimicking a 360 controller. Meaning that if a controller is registered but has additional buttons or axes, I'm not sure how the API would handle it, or even if I would be allowed to use those.
  2. If I were to use the Joystick API, how would I handle things such as menu navigation? Joysticks are not in any way standardized, so I literally cannot tell up from right or what button should be the confirm button. I could have some sort of popup window forcing players to map those controls immediately, but that strikes me as intrusive and inelegant.
  3. This is not specifically SDL related necessarily, but should controllers that look similar have the same mappings? For example if a player has both a 360 controller and a DualShock, and he remaps controls on his 360 controller, should those carry over to the DualShock, or should they be treated separately? If so, this would seem to be a boon for using only the GameController API, but things like arcade sticks also can be considered GameControllers, it depends on if SDL2 recognizes it. In that case, would I try to treat the arcade stick GameController different than the first two, or just roll with the same mapping for all GameControllers?

A lot of these questions come from my relative inexperience with PC gaming with a controller; I'm not entirely sure what players expect and demand from controller-related options. Any help you could provide would be greatly appreciated.

You have to support gamepads with unlimited buttons.

Just flag your buttons to the maximum number of buttons you use (-1).

Suppose i wanto support 2 buttons, for what gamepad dont matter, the buttons are repeating.

You only have to assign buttons to a function in the menu, press fire button, press bomb button, or assign a function to button A or B

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

Advertisement

The Windows API (XInput) that SDL wraps around only supports a limited number of buttons.  I believe it's 10, but don't quote me on that.  So regardless if a game controller comes out with 20 buttons, they will have to provide custom software to read those other buttons.  I started to look into going even lower level then XInput, but it was an undocumented mess and not really worth the effort in my eyes for the chance of a one off controller.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

I read into the HID API every OS supports (but differently) on USB driver level. It gives us all the capabilities to get not only information about axis and buttons but also gives a wieder access to keys and mouse. HID or raw interface works on windows using the Driver SDK or you wrap the functions you need into function pointer loading from the system library. You will also be able to use PnP events for input hardware.

It is a lot of code and without any convinience functions but worth it. (If you are interested in using HID on Windows, I can maybe write a tutorial or you look onto my GitHub)

Away from the platform specific code, all I do is getting a list of Stat structs out of processing function that handles the WM_INPUT message that looks like follows


/**
		 Defines an input type
		*/
		enum ControlType
		{
			Key = 0x1,
			Mouse = 0x2,

			Axis = 0x4,
			Button = 0x8
		};

		/**
		 A generic input report event
		*/
		#pragma warning( push )
		#pragma warning( disable : 4201)
		/**
		 An input state of certain type and data
		*/
		struct Stat
		{
			union
			{
				IHandle Code;
				struct
				{
					uint16 Type;
					union
					{
						uint16 Id;
						uint16 ScanCode;
					};
				};
			};
			union
			{
				struct
				{ 
					uint16 KeyStat;
					uint16 KeyCode;
				};
				uint16 Value;
			};
		};
#pragma warning( pop )

It is typed for the kind of input contained and anyways handles any kind of input event in a generic way so that you could either poll for some input (as for example Unity 3D does)


inline bool GetKeyDown(InputBuffer<Stat> const& stats, uint16 key)
{
	for(uint8 i = 0; i < stats.Size(); i++)
		if(HasFlag(stats[i].Type, Key) && stats[i].KeyCode == key)
			return (stats[i].KeyStat == 1);
	return false;
}

or handle your events and so a remapping for your inputs based on its unique ID.


inline uint64 GetInputId(Stat const& stat)
{
    return ((uint64)stat.Code << 32) | stat.Value; 
}

inline void BindInput(Stat const& stat, StaticContext<void ()> const& action)
{
    myInputBindings[GetInputId(stat)] = action; 
}
inline void CallInputTrigger(Stat const& stat)
{
    const StaticContext<void ()> action = myInputBindings[GetInputId(stat)];
    if(action)
       action.Invoke();
}

And you can of course save those IDs to a file


[Inputs]
Forward = 0xACBB2FD022000000
Backward = 0xFE45C270EF000000

 

Sorry for taking so long to get back to you guys on this.

I think we may be having a miscommunication issue here. The issue isn't getting the inputs themselves, SDL has very nice functionality in regards to that. The issue is coming up with sensible defaults when I don't know what each button or axis is. Obviously, the correct solution for handling this is to allow users to remap their controls, which is simple enough to implement, but what if they want to use their newly plugged in joystick to navigate to that menu. If the joystick is recognized by SDL, I can refer to the mapping provided by it to create a sensible default. If it isn't, I can think of no other option than blatantly interrupting the player and saying "Hey, I don't know what this is, help me out." This may be the best solution, but I was hoping someone who had experience with this issue would provide a better option.

Furthermore, should each joystick have an individual mapping, or not? I can imagine arguments being made for both cases, but again I'm not sure what players expect in terms of those options.

For now, I'm just using the GameContoller API; it's simple enough and based on what I hear from this thread or other sources, it theoretically should not be too hard to switch to using the Joystick API.

25 minutes ago, DrDeath3191 said:

but what if they want to use their newly plugged in joystick to navigate to that menu

Make them use the mouse / keyboard / standard-gamepad until they get to the controls configuration screen.

Many joysticks have "hats" as the API calls them -- which are usually D-Pads, etc. It's pretty safe to map these to up/down/left/right menu navigation when you don't know what the joystick is.

27 minutes ago, DrDeath3191 said:

it theoretically should not be too hard to switch to using the Joystick API

Don't switch - support both.
Xbox360/XboxOne controllers only work correctly under XInput ("GameController API"), while joysticks/steering wheels can work better under DInput ("Joystick API")

Advertisement

All joysticks has a name, so you could remember all previous configured settings if you like.

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

8 hours ago, Hodgman said:

Make them use the mouse / keyboard / standard-gamepad until they get to the controls configuration screen.

Many joysticks have "hats" as the API calls them -- which are usually D-Pads, etc. It's pretty safe to map these to up/down/left/right menu navigation when you don't know what the joystick is.

Yeah, I figured the hats would be safe. If people aren't bothered by that, it certainly makes my life easier. Thanks a lot!

8 hours ago, Hodgman said:

Don't switch - support both.
Xbox360/XboxOne controllers only work correctly under XInput ("GameController API"), while joysticks/steering wheels can work better under DInput ("Joystick API")

The GameController API is actually built on top of the Joystick API in SDL; it just has a database of recognized controllers whose axes and buttons it recognizes as "Left Analog X", "A Button", etc. So I could just access the mapping and treat it as a GameController with nothing lost. So using both or switching in the end would amount to the same thing, I think.

Upon further thought, I think individual mappings per controller, rather than a generic mapping would probably be easier to handle and allow for more options. So with that I think all of my questions have been answered! Thanks to everyone for the help!

This project might be of interest to you if you're planning on using the GameController API.

https://github.com/gabomdq/SDL_GameControllerDB

This topic is closed to new replies.

Advertisement