Just about every modern game lets the player customize the input according to their own preferences. This recipe will rely on jMonkeyEngine to do the work for us, and we will use Nifty GUI as a visual aid. We'll use RawInputListener
to work out which keys have been pressed and divide them between key codes and characters using the Keyboard
class.
The recipe will depend on there being some bindings in InputManager
. If you already have a game, this would not be a problem. If not, it will describe how to add a couple of bindings for the example to work.
Following the pattern from previous recipes, we'll start defining the controls, then move on to the screen, and finally work on the controller. Adding the controls and screen will consist of the following eight steps:
<nifty-control>
tag, we define a new <controlDefinition name="keyBindingControl">
.<panel childLayout="horizontal" width="80%" height="25px" backgroundColor="#666f" marginLeft="10px" marginRight="10px" marginTop="4px" align="center" >
<control name="label" id="#command" width="150px" text=""/>
<control name="button" id="#key" width="100px" valign="center"/>
width="*"
.<controlDefinition name="settingsControl">
that will contain a number of our keyBindingControls
.keyBindingControls
for each moving direction. The IDs of these controls should be representative of the direction and end with a key as follows:<control name="keyBindingControl" id="forwardKey"/>
gui.controller.SettingsController
settingsControl
class we just created should be added inside a layer elementThat's all with regards to XML. To create the Controller
class, perform the following steps:
NiftyController
. We call it SettingsController
.Element
fields for each of the key bindings we would like to track and one Element
field for the current selectedElement
.Map<Integer, String>
called mappings
where we can keep the relations between key inputs and input bindings.bindElements
method, which we'll define as well.mappings
map using the key code as key and the actual binding as the value. This can usually be found in the class that handles the input.forwardMapping = screen.findElementByName("forwardKey"); forwardMapping.findNiftyControl("#command", Label.class).setText("MoveForward"); forwardMapping.findNiftyControl("#key", Button.class).setText(Keyboard.getKeyName(KeyInput.KEY_W));
KeyEventListener
that implements RawInputListener
.onKeyEvent
, add an if
statement for if the incoming KeyInputEvent
is pressed and selectedElement
is not null.changeMapping
method and add the following line:selectedElement.findNiftyControl("#key", Button.class).setText(Keyboard.getKeyName(evt.getKeyCode()));
selectedElement
to null
.Now, we can turn our attention to the changeMapping
method.
mappings
map. If inputManager
of the application also has this, we should delete the old binding.mappings
map and check whether any of the bindings match the one that the selected element is handling. If you find a match, it should be deleted.KeyTrigger
class using keyCode
and add it to inputManager
using addMapping
.The last thing we need to do in this class is add an event subscriber to the buttons in keyBindingControls
.
keyClicked(String id, ButtonClickedEvent event)
, and give it the following annotation:@NiftyEventSubscriber(pattern=".*Key#key")
event.getButton().getElement().getParent()
to find out which one that is.This recipe explains that when a button that represents a key binding is clicked, the corresponding element is selected. By using a pattern in the annotation for the keyClicked
method, rather than an ID, we can capture all the keys using the wildcard.*
. This is also why the naming of the elements is important.
Once an element is selected, KeyEventListener
will start to listen for a key to be pressed on the keyboard. We set the text of the button to be the text representation of the key. In many cases, we can use the getKeyChar
method of KeyInputEvent
for this; however, not all the methods have a character like representation, hence the use of the Keyboard
class and getKeyName
method instead. This method tends to output a string representation instead.
The changeMapping
method first sees whether there is a current binding for the key pressed and deletes it if that is the case. This is not enough, however, since we also need to delete any previous bindings for that input. This is why we also iterate over the current mappings to see whether any of them match the binding that this key press was for; if yes, it deletes them too.
This recipe uses a static representation of the different input bindings. This would most likely be fine for many games, but modern first person shooters for example, can have 20 and more key bindings; adding all of these manually to the XML can be cumbersome and not good from a maintenance perspective. In this case, it might be better to use the Java Builder interface described in the Creating an inventory screen recipe to let Java do the repetitious work.
3.138.179.100