-
Notifications
You must be signed in to change notification settings - Fork 1
Handling user input
The input library is focused on mapping bindings. You, as a developer, don't need to know how the input is read. You shouldn't need to know what type of joystick is plugged in and its low-level button mapping. For this reason all our joystick bindings are named after the buttons on an XBOX 360-controller. This requires configurations for mapping generic controllers to XBOX 360-controllers which will be explained in CONFIGURING NEW CONTROLLERS.
Duality is a concept used in the library which means that it has a negative and a positive meaning. This is a very common concept for example turning right/left or accelerating forwards/backwards. A joystick axis is naturally dual but to make keyboard buttons dual you have to specify two buttons. A non-dual function is function like shoot, or jump. In these cases it doesn't make sense to have a negative value.
First of all we need to create a good way to manage our bindings. This will be done by creating an enum
with the underlying type int
as the library uses ints to identify mappings:
enum Functions : int {TURN,ACCELERATE,JUMP,SHOOT};
The enum
should be defined somewhere accessible from your entire project, preferably in its own header file. To create the bindings we must fetch the singleton ControlsManager and add the bindings. This should be done before the game starts. To add a binding we call the addBindings function. For example, if we want to map the turning function to be positive when D is pressed and negative if A is pressed we specify the following:
ControlsManager* cm = ControlsManager::getInstance();
cm->addBindings(TURN,{new KeyboardButton(sf::Keyboard::D,sf::Keyboard::A)});
The keys D and A are now mapped to the function turn. We would like to be able to turn using a joystick as well so instead we create the binding like so:
cm->addBindings(TURN,{new KeyboardButton(sf::Keyboard::D,sf::Keyboard::A),
new JoystickAxis(IJoystickTranslation::LEFT_THUMBSTICK_X,true)});
The second paremeter of JoystickAxis
says that this is dual, we have two states, left or right. Now what would happen if we specified one dual input and one non-dual button? An exception will be thrown and should at all times be avoided. The acceleration function is very similar to the turn function:
cm->addBindings(ACCELERATE,{new KeyboardButton(sf::Keyboard::S,sf::Keyboard::W),
new JoystickAxis(IJoystickTranslation::LEFT_THUMBSTICK_Y,true)});
The jump function is only one button:
cm->addBindings(JUMP,{new KeyboardButton(sf::Keyboard::Space),
new JoystickButton(IJoystickTranslation::A)});
The shoot button uses the right trigger from the controller as a non-dual axis (notice false
):
cm->addBindings(SHOOT,{new KeyboardButton(sf::Keyboard::Enter),
new JoystickAxis(IJoystickTranslation::RT,false)});
We can now present all mappings defined:
ControlsManager* cm = ControlsManager::getInstance();
try{
cm->addBindings(TURN,{new KeyboardButton(sf::Keyboard::D,sf::Keyboard::A),
new JoystickAxis(IJoystickTranslation::LEFT_THUMBSTICK_X,true)});
cm->addBindings(ACCELERATE,{new KeyboardButton(sf::Keyboard::S,sf::Keyboard::W),
new JoystickAxis(IJoystickTranslation::LEFT_THUMBSTICK_Y,true)});
cm->addBindings(JUMP,{new KeyboardButton(sf::Keyboard::Space),
new JoystickButton(IJoystickTranslation::A)});
cm->addBindings(SHOOT,{new KeyboardButton(sf::Keyboard::Enter),
new JoystickAxis(IJoystickTranslation::RT,false)});
}catch(std::string mixedDuality){
Logger::logError(mixedDuality);
//Terminate the game
}
To make the game do anything we have to read the status of the user input and perform the appropriate actions. Once again we start by fetching the ControlsManager
and from there we check the status.
ControlsManager* cm = ControlsManager::getInstance();
ControlStatus cs = cm->getStatus(TURN);
The ControlStatus
object contains information about all activators values. An activator is a device which produces input. Like the keyboard, joystick or mouse. The most common way to use the object is the function getValue()
which returns the value furthest away from zero from any activator. For example if you have tilted a joystick axis a bit and press a keyboard button mapped to the same function, the keyboard value will be returned as it always produces the max or min value. The value returned will always be between -100 and 100. If the function is non-dual the value will be between 0 and 100.
In a component which should control the ship we can use the status of the function:
void IComponent::update(float dt){
ControlsManager* cm = ControlsManager::getInstance();
ControlStatus cs = cm->getStatus(TURN);
if(cs.isActive())
turnVehicle(dt*maxRotation*cs.getValue()/100.0f); //we devide by hundred to transform
//the control-status value to percentages
cs = cm->getStatus(ACELERATE);
if(cs.isActive())
accelerate(max(maxAcceleration,oldAcceleration+dt*maxAccelerationIncrement*cs.getValue()/100.0f));
if(cm->getStatus(JUMP)->isActive())
jump();
}
A different component could check the status of the SHOOT
function and spawn a bullet when it is active.