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:
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.
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 |
---|---|
Returns | |
Returns | |
Returns | |
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). | |
Returns |
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.
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.
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 |
---|---|
The key is currently being held down. | |
The key went from being up to down in the last call to | |
The key went from being down to up in the last call to |
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! }
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.
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.
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.
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.
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).
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.
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 |
---|---|
Indicates that we are expecting an e-mail address to be entered. | |
Indicates that we are expecting a numeric value to be entered. | |
Indicates that the application will use the OS method for entering a password, possibly hiding characters as they are entered. | |
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.
18.188.137.58