Chapter 3. User Input

A video game is not going to be a whole lot of fun to play if the user has no way of controlling the events that happen, so in this chapter we will be looking at the various ways in which we can add interactivity to our programs by using Marmalade. By the end of this chapter you will know how to detect the following types of input:

  • Key presses
  • Touch screen and pointer inputs
  • Detection of gestures such as swipes and pinches
  • Changes in device orientation using accelerometers

Detecting key input

We'll start our journey into the world of player input methods with the simplest method possible—pressing keys, which we detect by using the s3eKeyboard API. To use these functions in our code, we just need to include the s3eKeyboard.h file.

While the touch screen may now rule supreme as the primary method of interacting with many modern devices, it is still worthwhile to know how to detect key presses. Android devices, in particular, have keys that are intended to be used for quick access to menus and for navigation around a program. Quite often these are not even physical buttons, just an area at the bottom of the touch screen, but they are still reported as key presses.

Key press detection is also extremely useful when debugging your code in the Windows simulator, as Marmalade allows full access to your computer's keyboard too. This makes it really easy to add a debugging functionality triggered by a key press.

The s3eKeyboard API allows us to detect key input either by key state or by character input. It also provides functionality that allows us to determine what kind of keyboard support the device we are running on has available.

Initialization and update of key information

There is a function called s3eKeyboardGetInt that allows us to find out what kind of keyboard our device has. We can use this information to provide different input methods to our program should we want to. For example, entering a user's name on a high score might allow the user to enter their name directly if the device has a full alphabetic keyboard, but could fall back to a method using arrow keys to cycle through characters if the device does not feature a full keyboard.

The s3eKeyboardGetInt function call takes a single parameter from the s3eKeyboardProperty enumeration, and returns an integer value. Details of the available properties are provided in the following table:

Property name

Description

S3E_KEYBOARD_HAS_NUMPAD

Returns 1 if the device has a numeric keypad, otherwise returns 0.

S3E_KEYBOARD_HAS_ALPHA

Returns 1 if the device has an alphabetic keypad, otherwise returns 0.

S3E_KEYBOARD_HAS_DIRECTION

Returns 1 if the device has directional controls (up, down, left, right, and a Select or OK button), otherwise returns 0.

S3E_KEYBOARD_NUMPAD_ORIENTATION

If the device has a numeric keypad, this property will return the orientation of the keypad relative to how the user is holding the device (if this is possible to detect).

S3E_KEYBOARD_GET_CHAR

Returns 1 if the device supports the character code input method or 0 if it does not.

The final value in this table can also be used with the function s3eKeyboardSetInt to show and hide the virtual keyboard on Android and iOS devices, which will then allow us to use the character code input method on these types of devices. The following function call will display the virtual keyboard:

s3eKeyboardSetInt(S3E_KEYBOARD_GET_CHAR, 1);

To hide the virtual keyboard, pass in 0 instead of 1.

Given that this feature is limited to just Android and iOS, and there is no way of determining whether the functionality is supported at runtime, this approach is probably best avoided if you intend to support a wide range of devices.

In order for our program to keep receiving updates on key presses, we must call the function s3eKeyboardUpdate in our code, once per game frame. The s3eKeyboard API keeps its own internal cache of the current key press states, which is updated when calling this function; so if we don't call s3eKeyboardUpdate frequently, we risk missing key press events.

Detecting key state

The most useful method of key detection for most arcade style games is to be able to discover the up or down state of any key on the device. The s3eKeyboard API provides two ways in which we can do this, these being polling the current key state and by registering a callback function.

Detecting key state changes using polling

We'll start with the simplest approach of polling for the current state of a key. It may be the simplest approach, but in most cases it is also the best approach as far as game coding is concerned, since often all we want to know is whether a key is currently pressed or released so that we can update our game state accordingly.

To detect the current state of any key on our device we make a call to s3eKeyboardGetState, which takes a value from the s3eKey enumeration (take a look at the s3eKeyboard.h file for a full list, but you can normally guess the name of the enumeration fairly easily—for example, s3eKeyUp is the up arrow key, s3eKey4 is the number 4 key, and so on) to identify the key we are interested in. The function returns an integer value that is a bit mask representing the current state of that key. The following key states can be detected by performing a bitwise AND operation on the return value:

Bit mask name

Description

S3E_KEY_STATE_DOWN

The key is currently being held down.

S3E_KEY_STATE_PRESSED

The key went from being up to down in the last call to s3eKeyboardUpdate.

S3E_KEY_STATE_RELEASED

The key went from being down to up in the last call to s3eKeyboardUpdate.

If the value returned from the function is zero, then the key can be assumed to currently be in the up position (that is, not being held) and has not just been released either.

The following code snippet shows how we would detect whether the number 3 key has just been pressed:

if ((s3eKeyboardGetState(s3eKey3) & S3E_KEY_STATE_PRESSED) != 0)
{
  // Number 3 key has just been pressed!
}

Detecting key state changes using callbacks

It is also possible to be informed whenever a key is pressed or released by using a callback function. Callbacks are preferred by many coders since they force us into writing smaller, more manageable functions that often yield a more concise and reusable solution. The polled approach to key detection may seem easier at first glance but it is easy to end up with a codebase that has key state checking logic spread across many source files. Using the callback approach will tend to ensure key handling code is implemented in a more structured way.

To set up a callback function that detects key state changes, we use the s3eKeyboardRegister function. We provide this function with the enumeration value S3E_KEYBOARD_KEY_EVENT to identify the type of callback we are setting up, a pointer to a function that will be the callback, and a void pointer that can be used to pass in our own custom data to the callback function.

When a key is pressed or released, the function we specified will be called. The callback function is passed a pointer to an s3eKeyboardEvent structure, which details the key press or release and is also provided with the custom data pointer we specified when registering the callback.

When we no longer wish to receive key state notifications, we can call s3eKeyboardUnRegister to disable the callback mechanism. We just need to pass the S3E_KEYBOARD_KEY_EVENT enumeration and the pointer to our callback method to stop the callbacks from occurring any more.

Here's a code snippet to illustrate how we might detect state changes to the number 3 key:

// Callback function that will receive key state notifications
int32 KeyStateCallback(s3eKeyboardEvent* apKeyEvent,
                       void* apUserData)
{
  if (apKeyEvent->m_Key == s3eKey3)
  {
    if (apKeyEvent->m_Pressed)
    {
      // Number 3 key has just been pressed
    }
    else
    {
      // Number 3 key has just been released
    }
  }
}

// We use this to register the callback function…
s3eKeyboardRegister(S3E_KEYBOARD_KEY_EVENT,
                    (s3eCallback) KeyStateCallback, NULL);

// …and this to cancel notifications
s3eKeyboardUnRegister(S3E_KEYBOARD_KEY_EVENT,
                      (s3eCallback) KeyStateCallback);

The method of key press detection to be used is really down to project requirements and personal preference. Since a call to s3eKeyboardUpdate will cache the state of every key for us, a polled approach may be best if we need to detect the current state of several keys at any time. A callback approach may be better if we just want to respond immediately to a key press and are less interested in tracking the key's state beyond this.

Detecting character code input

The s3eKeyboard API also provides support for reading character codes from the keyboard. With this approach, we don't receive any notification of when a key was pressed or released. Instead, we receive a stream of character codes which automatically take into account any special modifier keys; so if a user pressed the Shift key, followed by the A key, then released both these keys, we would only receive the character code for a capital letter A.

This approach is probably less useful for most games due to it not being an immediate form of notification, especially since fewer and fewer devices now feature physical keys that can be pressed.

Not all devices support this input method, so you should use a call to s3eKeyboardGetInt(S3E_KEYBOARD_GET_CHAR) to determine if it can be used.

For the sake of completeness though, let us look at how we can receive character codes using either polling or callbacks.

Detecting character code input using polling

To find out if a key that generates a character code has been pressed, all we have to do is call the following function:

s3eWChar lCharCode = s3eKeyboardGetChar();

The s3eWChar type is just an alternate type definition for the standard C++ type wchar_t, a wide character. While this type can vary in size, it is assumed to be a 16-bit value in Marmalade. When a key is pressed, its character code will be added to the back of a queue. Calling this function will return the character to the front of the queue, or S3E_WEOF if the queue is empty. We often call this function in a loop in order to try and keep the queue empty and not risk losing key presses.

The character codes returned will depend on the device you are running on, but in most cases the standard alphabet A through Z, numbers, and punctuation characters will be ASCII codes, just stored in a 16-bit value.

Detecting character code input using callbacks

Using the callback method of receiving character codes takes the same approach as the callback method for receiving key state changes.

We again use s3eKeyboardRegister and s3eKeyboardUnRegister to start and stop notifications from occurring, but we use the enumeration value S3E_KEYBOARD_CHAR_EVENT to indicate that it is a character code event we want to receive.

The callback function we provide will now be sent a pointer to an s3eKeyboardCharEvent structure that contains a single member of type s3eWChar named m_Char. This member will contain the character code that was generated by the user.

Note

Character code input is really only recommended if you are running on a device with a physical keyboard, as using virtual keyboards on touch screen devices can be unreliable with many key presses going unnoticed, particularly when characters outside the normal ASCII character set are entered (for example, Chinese or Japanese text entry).

Inputting strings

We've already seen how we can use the s3eKeyboard functionality to read character codes, but if we want to allow the user to enter a string and we don't mind our program forsaking its own user interface in favor of a standard modal string entry dialog, then we have a shortcut available to us.

The s3eOSReadString API makes string entry really simple; but it is not actually supported on every platform. To use this API we include the file s3eOSReadString.h, and then make a call to the function s3eOSReadStringAvailable to see if string entry functionality is available for use.

If we are able to use the API, then we have two functions at our disposal. The first is s3eOSReadStringUTF8, which will display a string entry dialog and return a UTF-8 encoded string as a const char pointer. The second method is s3eOSReadStringUTF8WithDefault, which allows us to also specify a UTF-8 string that will be used to populate the string dialog when it appears.

Note

UTF-8 is a widely used character format that allows full multilingual character support. It is often used when memory concerns are foremost, as single-byte characters such as the standard ASCII character set can still be represented in a single byte. Characters from outside the ASCII set (for example, Japanese Kanji) are encoded with two, three, or more bytes of information. One big advantage of UTF-8 is that you can continue to use null-terminated strings since it is guaranteed that a zero byte will never form part of a valid character code.

Both functions otherwise work in the same way. They both return a pointer to the string entered by the user (the API will take care of freeing this memory), or NULL if the user canceled the dialog.

They both also take an optional last parameter that can customize the layout of the string entry dialog. If the parameter is omitted or the value zero is passed, no restrictions are applied. The following table shows the other values that can be used:

Value

Description

S3E_OSREADSTRING_FLAG_EMAIL

Indicates that we are expecting an e-mail address to be entered.

S3E_OSREADSTRING_FLAG_NUMBER

Indicates that we are expecting a numeric value to be entered.

S3E_OSREADSTRING_FLAG_PASSWORD

Indicates that the application will use the OS method for entering a password, possibly hiding characters as they are entered.

S3E_OSREADSTRING_FLAG_URL

Indicates that we are expecting a URL to be entered.

When using these functions in an application, it is possible that the user may enter characters that we are then unable to process or display; this should be kept in mind, as generic string input may not always be a good choice (for example, you may be unable to display every possible character that can be entered using your game's font!).

Using this API will also likely break the look and feel of the game as its super whizzy UI is suddenly overlaid or replaced by a drab and boring system dialog.

These reasons, combined with the fact that it is not supported by all platforms, may mean that it is a better decision to implement our own in-game string entry routines. That being said, it is still a useful API to know about, if only for debugging purposes.

..................Content has been hidden....................

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