Posted by & filed under article.

Many games lets you customize the key bindings for movement, abilities, pause, exit and so forth. Some games utilize many key bindings and some times they are not placed in a suitable way for every one. Therefor the key bindings for movement might be set to the w,a,s,d keys by default and they make it optional to swap to different bindings, like utilizing the arrow keys instead. In this tutorial I will illustrate one way of creating a smooth options panel with Nifty GUI, so the user can easily assign new key bindings.

Set up the basic Nifty GUI boiler template.

Create a new XML file Interface/keyBindings.xml

<?xml version="1.0" encoding="UTF-8"?>
<nifty xmlns="http://nifty-gui.sourceforge.net/nifty-1.3.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://nifty-gui.sourceforge.net/nifty-1.3.xsd http://nifty-gui.sourceforge.net/nifty-1.3.xsd">
    <screen id="screenKeyBindings" controller="mygame.OptionAppState">

    </screen>
</nifty>

Here I have already added a screen with the id screenKeyBindings and set the controller to target OptionsAppState. The controller is necessary to add so the buttons later added know which class to interact with. The screen id is needed for a reference when we later want to update the content.

Utilizing Nifty GUI buttons

Since I want to use the costume buttons that comes with the Nifty GUI, I am required to add some style guidelines and the default controls. You can load these by adding the following two lines above the screen tag.

<useStyles filename="nifty-default-styles.xml" />
<useControls filename="nifty-default-controls.xml" />

I can now setup my button with the following XML markup:

<panel childLayout="horizontal" backgroundColor="#ffffff33" padding="5px">
 <text align="left" width="80%" textHAlign="left" font="aurulent-sans-16.fnt" color="#ffff" text="Move forward" />
 <control width="20%" id="forward" name="button" label="">
 <interact onClick="setKeyBinding(forward)"/>
 </control>
</panel>

nifty-button-markup

The button is encapsulated with a surrounding panel. Inside the panel is followed by a text description and a button. The button nests a interact that will trigger the function setKeyBinding in the mygame.OptionAppState. Notice that the button ID is set to forward and that the setKeyBinding takes the exact ID as parameter. This is important to identify which button was clicked and to later update the button label.

OptionAppState

To organize the game we create a new AppState named OptionAppState to handle the key binding. First we setup the Nifty GUI, this is straight forward like any Nifty GUI implementation. In this case its wrapped nicely in the method initNifty(). It uses the file keyBindings.xml and sets the screen with id screenKeyBindings.

private Nifty nifty;
private NiftyJmeDisplay niftyDisplay;
private void initNifty() {
  niftyDisplay = new NiftyJmeDisplay(assetManager,
									   inputManager,
									   audioRenderer,
									   guiViewPort);
  nifty = niftyDisplay.getNifty();
  nifty.fromXml("Interface/keyBindings.xml", "screenKeyBindings", this);

  guiViewPort.addProcessor(niftyDisplay);
}

Whats gonna happen when we click the button? At the moment we have setup the Nifty GUI with a button that calls setKeyBinding. When this action is triggered we want to start listing to keyboard event. When and if a key is pressed, we want to bind the pressed key to the button we clicked, update the button label with the key we pressed and stop listening to the keyboard.

To achieve this we create a new class named SettingsInputHandler that implements RawInputListener. The class uses the onKeyEvent to grab the key event and send the key event back to the OptionsAppState. We pass the eventId along the way, this is the ID to the button pressed.

public SettingsInputHandler(OptionsAppState appState, String eventId) {
  this.appState = appState;
  this.eventId = eventId;
}

public void onKeyEvent(KeyInputEvent evt) {
  try {
    appState.keyBindCallBack(evt, eventId);
  } catch (Exception ex) {
  }
}

This will be even more clear when we setup the interact setKeyBindings.

private SettingsInputHandler sih;
public void setKeyBinding(String eventId) {
  Screen screen = nifty.getScreen("screenKeyBindings");
  if(sih != null) {
    Button button = screen.findNiftyControl(sih.getEventId(), Button.class);
    button.setText("");
    inputManager.removeRawInputListener(sih);
  }
  Button button = screen.findNiftyControl(eventId, Button.class);
  button.setText("<press any key>");
  sih = new SettingsInputHandler(this, eventId);
  inputManager.addRawInputListener(sih);
}

We setup the SettingsInputHandler as a private variable outside of the method. When the button is clicked the button label is set to “<press any key>”. We initialize the SettingsInputHandler with OptionsAppState and button ID as parameters. Then we add the the SettingsInputHandler to the inputManager as a raw input listner. Notice that if sih is not null, we will clear the pressed button’s label and remove the listener. We need to handle this in case multiple buttons are pressed with out any keyboard interaction.

Back to the flow, a button is clicked. SettingsInputHandler is added to the inputListner. When a key is pressed a callback is sent to the OptionsAppState to the method keyBindCallBack.

public void keyBindCallBack(KeyInputEvent evt, String eventId) {
  /* Callback, triggered from settingsKeyHandler */
  Screen screen = nifty.getScreen("screenKeyBindings");
  Button button = screen.findNiftyControl(eventId, Button.class);
  button.setText("" + KeyBindings.getKeyName(evt.getKeyCode()));

  /* Performe mappings from Nifty GUI to KeyBindings */
  mapNiftyBindings(eventId, keyBindings, evt.getKeyCode());

  inputManager.removeRawInputListener(sih);
  sih = null;
}

The method keyBindCallBack uses the eventId to find the button pressed and update the label with what key that was pressed. Then we need to assign the pressed key to the variable that is referenced threw out the game. This assignment is handled in the method mapNiftyBindings.

KeyBindings

The KeyBindings class that contains our key references and should be accessible to any AppState that handles input. By default the keys defined can be assign a value, but this is not required.

// Default bindings
public int FORWARD = KeyInput.KEY_UP;
public int BACKWARD = KeyInput.KEY_DOWN;
public int EXIT_APPLICATION = KeyInput.KEY_ESCAPE;
public int PAUSE;

This class has a static method getKeyName that traverses the methods of the class KeyInput from JME3 and returns the reference name for the key value. So when the spacebar is pressed KEY_SPACE is returned and not an empty space.

public static String getKeyName(int keyCode) {
 Class keyClass = KeyInput.class;
 for (Field field : keyClass.getFields()) {
   try {
     if(keyCode == field.getInt(null)) {
       return field.getName();
     }
   } catch (Exception ex) {
     // Shh
   }
 }
 return null;
}

The final result:

nifty-button-markup-multiple

You can download the source files here.

2 Responses to “Nifty GUI create key bindings for your game in JMonkey Engine 3”

  1. gfdgd

    interesting, but
    – why do you put the src files in the asset folder ?
    – you dont even assing the bindings to the inputmanager action listener

    Reply

Leave a Reply

  • (will not be published)