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.
For this recipe, it is recommended that you have the sample projects available from the Eclipse workspace.
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.
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.
3.147.126.211