0
\$\begingroup\$

I am trying to brainstorm how to create customizable input. Any help would be greatly appreciated, but here's my best guess so far.

I could create a custom 'Input' class, with Methods to call for each input. Like public bool IsRight()", "public bool IsJump()", etc. Then I could call these methods from any other class.

However, I cannot figure how I would assign these different methods to different keys such that:

public bool IsRight(){
    if (keyboardState.IsKeyDown(Keys.Right)){
        return true;
        }
    return false;
}

works, but I could change the input from 'Right' to 'D', or so on.

\$\endgroup\$
1
\$\begingroup\$

Gabriele's answer provides a pretty neat way to handle input generically, but for rebindable keys I don't personally prefer the callback approach, since I don't like having to maintain that map of callbacks whenever keybindings change.

Disclaimer: Following answer is in C++ but most of the concepts should be applicable to other languages as well.

Instead, I essentially just store controls in a struct:

struct Controls
{
    Key m_MovementUp;
    Key m_MovementDown;
    Key m_MovementLeft;
    Key m_MovementRight;

    Key m_Shoot;
}

Every input device (you could use inheritance to a struct ControlsBase and add a similar struct for controller buttons) holds an instance of the controls struct. When the device is initialized, you could load the keys from a file to the members, and at shutdown you could write the keys to the file. Your settings menu can build displays for each input device, and alter the values of the control keys.

The abstraction of "input device" is not necessarily necessary, since your input manager could perhaps just map the controls to an index (std::map<PlayerID, Controls*> m_Controls), and query those from the gameplay code:

Player::Player(PlayerID id)
{
    m_Controls = InputManager::GetControls(id);
}

void Player::Update()
{
    if (keyboardState.IsKeyDown(m_Controls->m_MovementRight))
    {
        MoveToRight();
    }
}

Since m_Controls is a pointer to the global controls struct, the gameplay code will automatically use the right key if the player for an example rebinds keys from the pause menu.

\$\endgroup\$
0
\$\begingroup\$

(There might be a better way, but this is what i've come up with; This approach is inspired by Unity)

Ref link: https://github.com/gabrielevierti/Advent3D/blob/master/Core/Core/src/Logic/input.h

As you can see, i made an enum, called keycode, which lets me choose which key on the keyboard is pressed by the user. There is also a boolean array, which contains the state of each key. By creating a 'getKey(KeyCode)' type of function, we can get the keycode from the 'KeyCode' enum, and the corresponding boolean in the 'keys' boolean array will be set to it's state(either true -> pressed; false-> released). The same approach can be applied to Buttons, like mouse buttons or joystick buttons, joystick axis, and basically any type of input, VR too for example.

If we want to check if the X key was pressed, all we do is:

If(Input::GetKey(KeyCode.X))
{
    //the X key was pressed
}

I'm using GLFW as a base, and I'm deriving from it. This is not the smartest way to do it, since we are just "renaiming" GLFW values to ours, but hey it's still a valid option.

Map Based approach

As suggested to me in the past (from Tyyppi77), you could use a map based approach: you basically store a map of function callbacks, that get invoked when a specific key is pressed.

class Input
{
public:
using Callback = std::function<void()>;

void addBinding(int key, const Callback& callback)  
{
    m_Callbacks[key].push_back(callback);
}

void onKeyPress(int key)
{
    for (Callback& callback : m_Callbacks[key])
    {
        callback();
    }
}
private:
std::map<int, std::vector<Callback>> m_Callbacks;
};

This way you can add callbacks based on your needs, just by creating std::functions. You could call onkeypress(), specify the correct key, and the corresponding callback will get invoked.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.