Managing multiple listeners at once

So far, we have studied the two basic mechanisms that Libgdx provides when it comes to handling user input. An event-based approach can be quite useful to respond to certain actions. However, only one InputListener can capture events at a given time.

Often, you will find yourself with two separate subsystems that are interested in different events. Maybe the player input controller, UI system, and game screen all want to be informed whenever the user does something. How will you tackle this limitation then?

You will be pleased to know that Libgdx also comes with a way to achieve this with minimal hassle. Throughout this recipe, we will cover input multiplexing and will have two classes listening to events at once.

Getting ready

For this recipe, it is recommended that you have the sample projects available from the Eclipse workspace.

How to do it…

Take a look at the InputMultiplexerSample class, which looks very similar to the sample from the previous recipe. At the bottom of the file, we have two inner classes called InputHandlerA and InputHandlerB. Both of these extend the InputAdapter stub class. However, each one of them overrides different methods; while A is interested in the key-related events, B only cares about touch- and mouse-related events.

Two InputProcessor objects can handle the same event through a multiplexer.

The following listing illustrates our description. Implementation details have been stripped for reasons of space. They simply call the addMessage() method of InputMultiplexerSample, providing the event data for logging purposes:

private class InputHandlerA extends InputAdapter {
  public boolean keyDown (int keycode);
  public boolean keyUp (int keycode);
  public boolean keyTyped (char character);
}

private class InputHandlerB extends InputAdapter {
  public boolean touchDown (int screenX, int screenY, int pointer, int button);
  public boolean touchUp (int screenX, int screenY, int pointer, int button);
  public boolean touchDragged (int screenX, int screenY, int pointer);
  public boolean mouseMoved (int screenX, int screenY);
  public boolean scrolled (int amount);
}

To distinguish between the two systems more clearly, we have a ScreenLogMessage class, which simply holds a string message and a color. InputHandlerA uses yellow, while InputHandlerB goes for green, shown as follows:

private class ScreenLogMessage {
  public final String message;
  public final Color color;

  public ScreenLogMessage(String message, Color color) {
    this.message = message;
    this.color = color;
  }
}

Our InputMultiplexerSample class starts from where InputListeningSample left, and it adds a InputMultiplexer object and an array of ScreenLogMessage references. The multiplexer will hold references to both our listeners and will distribute all input events between them:

private InputMultiplexer multiplexer;
private Array<ScreenLogMessage> messages;

In the create() method, we need to pay attention to what we do with the multiplexer. We set it as our input processor in the Libgdx input system and then add new instances of our InputHandler classes to the multiplexer:

public void create() {	
…

  messages = new Array<ScreenLogMessage>();
  Gdx.input.setInputProcessor(multiplexer);
  multiplexer.addProcessor(new InputHandlerA());
  multiplexer.addProcessor(new InputHandlerB());
}

Let's quickly move on to the render() method. Here, we simply iterate through the ScreenLogMessage objects, rendering them on the screen using our bitmap font. Messages are displayed at a fixed location along the x axis while we decrease our y axis value so as to stack them on top of each other:

public void render() {	
…

  batch.begin();
  for (int i = 0; i < messages.size; ++i) {
    ScreenLogMessage message = messages.get(i);
    font.setColor(message.color);
    font.draw(batch, message.message, 20.0f, VIRTUAL_HEIGHT - 15.0f * (i + 1));
  }
  batch.end();
}

Whenever an event is caught by one of the handlers, it calls the addMessage() method of the enclosing class. This basically adds the message to the array and removes the oldest one if in case there are too many elements already:

private void addMessage(String message, Color color) {
  messages.add(new ScreenLogMessage(message, color));

  if (messages.size > MESSAGE_MAX) {
    messages.removeIndex(0);
  }
}

Feel free to run the sample; after a few movements of the mouse and keystrokes, you will see which processor handles each event.

How it works…

InputMultiplexer is nothing more than a special InputProcessor interface implementation; feel free to see this by yourself in the Libgdx source code. Your own InputProcessor implementations can be registered with it and removed when required. The multiplexer will simply channel all the events to the registered listeners.

There is one subtlety to take into account though. You may have noticed how event handlers are supposed to return a Boolean value. If a handler returns true, it means that the event has been processed and will not be sent to the next processor. Intuitively, if it returns false, the notification process will carry on as normal:

public boolean keyDown (int keycode)

Consequently, the order in which the InputProcessor objects are added to a multiplexer matters. Do you want your UI system to have the chance to decide whether or not it is interested in a certain event before the player controller takes over? This can also happen within a complex UI system, where some widgets can capture an event and prevent it from being broadcasted. We will cover this in Chapter 8, User Interfaces with Scene2D.

See also

  • After this recipe, most of your input processing related needs should be pretty much covered. Going further, topics are slightly more complex but also exciting. Carry on and read the Detecting more complex gestures recipe.
..................Content has been hidden....................

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