InputMultiplexer consumption limits when Stage and custom processors compete
0 reputation · 07 Jan 2023, 20:37 UTC
0 reputation · 07 Jan 2023, 20:37 UTC
When a Scene2D Stage is registered in an InputMultiplexer alongside a custom InputProcessor, the precise point at which event consumption occurs and how it propagates to remaining processors depends on backend-specific event queue ordering. The documented behavior states that returning true from an InputProcessor consumes the event, preventing subsequent processors from receiving it, yet Stage actor listeners and multiplexer ordering interact in ways that are not uniformly specified across LWJGL, Android, and GWT backends. This creates ambiguity for games that rely on discrete UI input from Stage actors while simultaneously processing continuous polling from Gdx.input in the same render loop.
Constraints include variation in callback delivery timing and available event types per platform, as well as the silent starvation of downstream processors when an upstream actor or processor returns true. Additionally, touch and mouse coordinates are provided in screen space, requiring Camera.unproject for world-space comparisons, and incorrect unprojection can produce input logic errors that mimic consumption failures.
What exact conditions cause a Stage actor listener to consume an event and block subsequent InputMultiplexer processors? Does the relative registration order of the Stage and a custom processor determine which events are consumed first? How do backend-specific event queue implementations differentiate between actor-consumed events and processor-consumed events on desktop versus Android?
34775 reputation · 07 Jan 2023, 21:47 UTC
An event is consumed the moment any processor in the chain returns true from its handler, and dispatch stops immediately — processors registered later never see it. A Stage actor listener consumes an event only when three conditions hold: the actor is hit at the event's screen coordinates (Stage.hit() finds it), the actor is visible and touchable, and its listener returns true. Registration order in the InputMultiplexer is the entire precedence system: whichever processor is added first gets first refusal of every event.
InputMultiplexer iterates its processors in insertion order for each event. The first true return aborts the loop for that event.Stage is itself an InputProcessor. Its touchDown/touchUp etc. forward to actors under the pointer. If an actor's InputListener returns true, the Stage's handler returns true, so the multiplexer treats it as consumed.Touchable.disabled, or outside the hit point do not consume anything, and the Stage then returns false, letting the event continue down the chain.true rule is consistent across LWJGL, Android, and GWT backends. What varies per backend is event timing, touch coalescing, and pointer semantics — not the consumption contract.Backend queue differences do not change whether an event is consumed, but they can change which events exist in a frame. Android may coalesce rapid move events; desktop delivers mouse and keyboard events that have no touch equivalent. If your custom processor polls Gdx.input continuously in render(), that polling is independent of the multiplexer entirely — consumption only affects dispatched callbacks, not polled state. Apparent "consumption failures" are very often coordinate bugs: Stage and multiplexer callbacks deliver screen-space Y-down coordinates, and forgetting camera.unproject() (or unprojecting with the wrong viewport) makes world-space hit tests miss, which looks exactly like an actor swallowing events.
true.multiplexer.addProcessor(stage) before vs. after your custom processor — and confirm delivery changes. If it doesn't, something else (often a GestureDetector wrapping a processor) is in the chain.stage.hit(Gdx.input.getX(), Gdx.input.getY(), true) at the moment of the event and compare with the actor you expect. A null or unexpected result points to unprojection or touchable-bounds problems, not multiplexer ordering.// UI gets first refusal; game logic sees only what actors reject
InputMultiplexer mux = new InputMultiplexer();
mux.addProcessor(stage); // actor listeners consume first
mux.addProcessor(gameInput); // receives only unconsumed events
Gdx.input.setInputProcessor(mux);The one detail that would change this recommendation: whether your custom processor needs events even when the UI consumes them. If so, don't rely on order — have the actor listener return false after handling, or forward the event manually, since the multiplexer has no "observe without consuming" mode.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.