Customizing the input and settings page

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.

Getting ready

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.

How to do it...

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:

  1. Inside a <nifty-control> tag, we define a new <controlDefinition name="keyBindingControl">.
  2. Here, we'll add a horizontal spanning panel with some margin to the edges of its container and enough height to contain text:
    <panel childLayout="horizontal" width="80%" height="25px" backgroundColor="#666f" marginLeft="10px" marginRight="10px" marginTop="4px" align="center" >
  3. This panel will have three elements. The first is a label control that contains the text for the key binding, as shown in the following code:
    <control name="label" id="#command" width="150px" text=""/>
  4. Then, it will have a button to change the binding, displaying the current key:
    <control name="button" id="#key" width="100px" valign="center"/>
  5. In between them, it will have a simple panel with width="*".
  6. Now, we can define another <controlDefinition name="settingsControl"> that will contain a number of our keyBindingControls.
  7. This will contain a panel, and inside this, four 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"/>
  8. The following points are needed for the screen:
    • The ID should be settings, and controller should be gui.controller.SettingsController
    • The settingsControl class we just created should be added inside a layer element

That's all with regards to XML. To create the Controller class, perform the following steps:

  1. As usual, we create a new class that extends NiftyController. We call it SettingsController.
  2. We'll have Element fields for each of the key bindings we would like to track and one Element field for the current selectedElement.
  3. In addition, we should add Map<Integer, String> called mappings where we can keep the relations between key inputs and input bindings.
  4. From here, we should call a bindElements method, which we'll define as well.
  5. Inside this, we'll add the current key bindings to the 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.
  6. Next, for each of the keys we would like to handle, we find the reference in the settings screen and populate their values accordingly. For example, for the forward key use the following code:
    forwardMapping = screen.findElementByName("forwardKey");
    forwardMapping.findNiftyControl("#command", Label.class).setText("MoveForward");
    forwardMapping.findNiftyControl("#key", Button.class).setText(Keyboard.getKeyName(KeyInput.KEY_W));
  7. Next, we define a new inner class called KeyEventListener that implements RawInputListener.
  8. In onKeyEvent, add an if statement for if the incoming KeyInputEvent is pressed and selectedElement is not null.
  9. Here, we add a reference to the yet-to-be-created changeMapping method and add the following line:
    selectedElement.findNiftyControl("#key", Button.class).setText(Keyboard.getKeyName(evt.getKeyCode()));
  10. Finally, we should set selectedElement to null.

    Now, we can turn our attention to the changeMapping method.

  11. This method has the pressed key code as an input parameter, and we use this to see whether we already have a binding in our mappings map. If inputManager of the application also has this, we should delete the old binding.
  12. Next, we need to iterate through all the values in our 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.
  13. Finally, we create a new 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.

  14. We define a new method, keyClicked(String id, ButtonClickedEvent event), and give it the following annotation:
    @NiftyEventSubscriber(pattern=".*Key#key")
  15. When the button is clicked, the corresponding element should be selected, so we use event.getButton().getElement().getParent() to find out which one that is.

How it works...

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.

There's more...

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.179.100