Skip to content

Development : Mallet Engine : Input

July 23, 2012

The Mallet Engine supports a multitude of different platforms, in particular Linux, Windows, Mac, and for mobile: Android. Java provides excellent cross-platform  support by enabling a standard interface for interacting with system level components. To gain user-input you simply implement the appropriate Listeners (Key, Mouse, MouseMotion, MouseWheel) and then register them with the appropriate Window, typically a JFrame. Android deals with user-input differently so we will focus on the Desktop implementation of Mallet Engines’ Input System.

Java provides a fairly consistent API and coding for cross-platform is almost built into the language by default. However, when you involve Android you cannot bank on being able to use the entirety of the Java API. To resolve this issue, you must implement an abstraction layer that takes the data from one API and converts it to a standard that the rest of your system adheres to. The Mallet Engines’ Input Framework is designed for this purpose in mind, it takes user-input from X API and massages the data into the Mallet Engines’ input standard.

All Input Systems used by the Mallet Engine implement the Input System Interface, which defines how the engine as a whole will interact with it. The interface is made up of five methods:

addInputHandler( final InputHandler )
removeInputHandler( final InputHandler )
update()
clearHandlers()
clearInput()

The first method is how an external component can register an interest in user-input, and as you have probably guessed the second method is how it can be removed from receiving data. The update method is used to send the input events to the registered input handlers. In a typical game-state, update, is called as many times as possible to ensure the latest input is passed  as soon as possible. The fourth method is to un-register all Input Handlers from the Input System, while the last method clears the current queue of inputs waiting to be passed to the Input Handlers.

The Input System massages user-input into an Input Event object, which can handle a range of input-types, including: mouse, keyboard, and touch. At the moment Input Event supports two-button mouses and supports scroll wheel events. Keyboard events are registered as either pressed or released, and provides the character of the key (if available) and its keycode. Touch events are handled similarly to mouse events, but touch has its own three input-types: move, up, and down.

In an older version of the Desktop Input System all user-input was massaged, stored in a queue, and then passed to the Input Handlers at the appropriate time. However, it quickly became apparent that the Linux version of the Java Listeners had a bug. By default when you press a key an event is sent to the appropriate listener, when you release another event is released. For Linux, when a key was pressed a torrent of repetitive identical key presses were passed which resulted in our queue being filled with irrelevant Input Events.

The problem was resolved by mapping the keycode to a Key State object. The Key State stored an input event, it also identified whether the key-state had changed and stored the last time stamp of the key. When a key event came from a Java Listener the time-difference was compared, if the difference was greater than zero, the input event was updated and the Key State identified itself as changed. During the update phase it would loop through the map only passing input events that had changed. This fix eliminated the irrelevant creation of new Input Events, and the passing of repetitive data to the game.

Luckily Mouse events are not affected by the same bug, but, it does have its own set of problems. The mouse event that come from a Java Listener provide the mouse co-ordinates in relation to the window, however, you could be playing your game in a 1024×768 window, but the viewport could be 800×600. For the mouse to be displayed correctly, we must convert it to the co-ordinate system of our renderer.

This solution is provided through the Render Info class that implements the Input Adapter Interface, allowing you to convert a point in the display co-ordinate system to a point in the render co-ordinate system. The conversion will also translate the point by the camera-position to ensure it is in the expected location.

float convertInputToRenderX( final float _x )
float convertInputToRenderY( final float _y )
Vector2 convertInputToRender( final Vector2 _input )

For many games, particularly two-dimensional ones, they have a desired aspect-ratio (4:3, 16:9, etc).  If the display-ratio is not the same, then the game can appear squashed or stretched. A solution to this problem is to ensure that your game is not dependant on a particular ratio and that content on the periphery is not of great concern to the player. Sometimes, the aspect-ratio is greatly important and must be consistent irrelevant of the display ratio. The solution is to only allow the render to be scaled to the maximum resolution the display-ratio will allow, while ensuring the render keeps its aspect-ratio, creating a letterbox, or pillarbox effect. Render Info deals with this effect and takes into account the offset caused while converting the mouse co-ordinates.

float convertInputToRenderX( final float _x )
{
    return ( ( ( _x - screenOffset.x ) * renderDimensions.x ) / scaledRenderDimensions.x ) - cameraPosition.x ;
}

float convertInputToRenderY( final float _y )
{
    return ( ( ( _y - screenOffset.y ) * renderDimensions.y ) / scaledRenderDimensions.y ) - cameraPosition.y ;
}

_x/_y : The mouse position.

screenOffset : Takes into account the pillar/letterbox effect. If there is no black-bars then this will be zero.

renderDimension : The original resolution of the final rendered frame.

scaledRenderDimension : The resolution the frame will be scaled to when displayed on screen.

The Android Input System operates in a very similar manner except how it massages the input-data. At a later date I would like to improve the system by reducing the amount of Input Events it creates. Currently a new Input Event is created for each mouse/touch event and then discarded soon after. I would like to implement a pool of reusable Input Events to alleviate a potential garbage collection nightmare.

Ross.

Advertisements
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: