Inside InputSprocket
Volume Number: 15
Issue Number: 1
Column Tag: Games Programming
Inside InputSprocket
by Brent Schorsch
How to Use InputSprocket to Support User Input in
Games
Introduction
Handing user input is a necessary aspect of a game. Without user input, it is not a
game, but just a flashy movie. This article will cover what a game developer needs to
do in order to handle user input using InputSprocket, Apple's gaming input API.
InputSprocket was created to address the lack of a common API for joysticks and other
gaming devices on the Macintosh platform. With the introduction of the iMac and USB,
InputSprocket has opened the door for numerous gaming hardware manufactures to sell
their products to Macintosh customers. Game developers also benefit by supporting
InputSprocket because their game will support the latest input gadgets with no extra
effort from the game developer. Game players benefit from the increased options.
The Basics
InputSprocket is a relatively simple API to use. There are some more complicated
aspects, which will be covered later, but the basics are straightforward. First, an
application must provide a list of its input needs to InputSprocket. Example needs a
game might have are: fire weapon, jump, turn, look, rudder, lower landing gear. The
list of needs determines how the user can interact with the game and therefore will
directly affect whether the game is thought to be hard to control or very intuitive and
flexible.
Once InputSprocket is initialized and has the list of needs, there is a single API call
(ISpConfigure) which will bring up a user interface, provided by InputSprocket, for
the user to map the physical elements of the device to needs in the game. InputSprocket
maintains the user's settings in a preference file. This dialog is pictured in Figure 1.
During game play, the application can either get events or poll the current value for
any of these needs. Typically, applications will get events on 'button kind' needs (and
others) and poll the value of 'axis kind' needs once per game loop. The different 'kinds'
of needs is covered later.
Figure 1. InputSprocket Configure Dialog.
The 'Low Level' Interface
In fact, there are two different ways an application can use InputSprocket: 'low-level'
and 'high-level'. The 'low-level' interface provides a means for the developer to get a
list of the devices currently available and manually check the state of each device. This
interface is strongly discouraged. It is provided for those developers who wish to
provide their own user interface to configuring device, but it puts a much greater
burden on the game developer. With this interface, the game developer is responsible
for determining how each kind of device might be configured to work with their game
and to provide a complete user interface to make this configuration. Games that use the
'low-level' interface lose any extra functionality that is provided by the high level
interface. The rest of this article will focus on the 'high-level' interface, which is the
one that all game developers are encouraged to use.
Determining the Game's Needs
There are four basic need kinds: button, direction pad (dpad), axis and delta. (There is
also a movement kind, but its use is discouraged.) Each kind has a different data
format. The button kind is the simplest, with values of up or down. A dpad has nine
possible values: idle, left, up-left, up, up-right, right, down-right, down, and
down-left. An axis kind's value is a 32 bit unsigned number, regardless of whether it
is symmetric. Finally, the value of a delta type is a 32 bit Fixed point number of
inches moved.
When deciding what kind to make a particular need, one should prefer the axis kind to
button and dpad kinds when applicable. This will allow game players to get the full
functionality out of their equipment. Sure, keyboard users may only be able to either
walk or run (forwards or backwards), but if someone has a device with enough 'analog'
inputs, the game should let him continuously vary his speed. In some cases, it may be
necessary to provide some extra information to InputSprocket so that the keyboard
user experience is maintained which is covered later. Delta kinds are provide data
similar to 'raw mouse' data but due to limitations in the current InputSprocket
drivers, are most useful in the more complex InputSprocket cases, where once again
extra information is provided.
In some cases, it may make sense to provide several needs that affect the same thing in
the game world. For example, in addition to a 'next weapon' and 'previous weapon' need,
it often makes sense to provide needs to switch to specific weapons, e.g. 'use machine
gun', 'use plasma rifle'. The code necessary to support these different means to switch
weapons is trivial, but the added functionality to certain users (with the right
hardware) is immense. The speech recognition InputSprocket driver does not add much
if you can only say 'next weapon', but if you can say 'use plasma rifle', then it might
start to be appealing. Another case where multiple needs might make sense is where
the need is some form of toggle, such as 'map mode' or 'landing gear'. One button may be
sufficient for most users, but if the hardware physically has two states, like the caps
lock key on some keyboards or any sort of lever, then it is very easy for the physical
device to become out of sync with the game. However, by adding two more needs, switch
to map view (or landing gear up) and switch to non-map view (or landing gear down),
it is impossible, when properly configured, for the hardware to be out of sync with the
game, regardless of the starting state of the game and the hardware.
Finally, it often makes sense to use the native data types as input for the game. This is
a case where the game essentially gives InputSprocket extra information so that it can
behave better. It does this by providing multiple needs, of different data kinds, to
change the same thing in the game state. An example where this is useful is for the
traditional rudder controls on a flight-sim. Typically, the keyboard commands to
change the rudder are 'Increase Left Rudder', 'Center Rudder', and 'Increase Right
Rudder'. Each time either of the 'Increase ...' keys are pressed, the current rudder
value is changed. In order to restore the plane back to no rudder, you have to either
manually press the keys enough times to get back to center, or press the 'Center
Rudder' key. The current 'InputSprocket Keyboard' driver does not allow the user to
configure an axis need to three keys like this, so this case calls for using extra needs.
In addition to the rudder axis need (for those who have a device such as a rudder or
twisting joystick), the game will have three button needs: left rudder, center rudder,
and right rudder. The axis need should set the kISpNeedFlag_Axis_AlreadyButton bit in
the ISpNeed, flags field and the button needs should set the
kISpNeedFlag_Button_AlreadyAxis bit. Another common case for using button and axis
types is for a throttle in a flight-sim.
Listing 1 contains the complete list of needs chosen for the sample application. Listing
2 is an excerpt from the sample application which demonstrates using axis and button
types to change yaw angle. The kISpNeedFlag_Button_AlreadyDelta bit is also set,
because the sample application also reads delta types separately. Typically, delta types
are used when the developer wants the 'feel' of using a mouse to be similar to how it
typically feels in a first person shooter, like Unreal or Quake.
Listing 1: ISp_Sample.h
This is an excerpt from ISp_Sample.h containing the enumeration
of all the needs.
enum
{
// primary needs

kNeed_FireWeapon,
kNeed_NextWeapon,
kNeed_PreviousWeapon,

kNeed_Roll,
kNeed_Pitch,
kNeed_Yaw,
kNeed_Throttle,

kNeed_StartPause,
kNeed_Quit,

// secondary needs for alternative input kinds

kNeed_Weapon_MachineGun,
kNeed_Weapon_Cannon,
kNeed_Weapon_Laser,
kNeed_Weapon_Missle,
kNeed_Weapon_PrecisionBomb,
kNeed_Weapon_ClusterBomb,

kNeed_Roll_AsDelta,
kNeed_Pitch_AsDelta,
kNeed_Yaw_AsDelta,

kNeed_Yaw_Left,
kNeed_Yaw_Center,
kNeed_Yaw_Right,

kNeed_Throttle_Min,
kNeed_Throttle_Decrease,
kNeed_Throttle_Increase,
kNeed_Throttle_Max,

kNeed_NeedCount
};
Listing 2: ISp_Sample.c
Input_Initialize
This is an excerpt from Input_Initialize which initializes
the needs array.
//* we'll init all the player 1 items now
//* (everything but quit unless we add a second player)
tempNeed.playerNum = 1;
// [snip - code removed from this listing]
// Now group 4, which is for changing yaw
tempNeed.group = 4;
GetIndString (tempNeed.name, kSTRn_NeedNames,
kNeed_Yaw + 1);
tempNeed.iconSuiteResourceId = 1000 + kNeed_Yaw;
tempNeed.theKind = kISpElementKind_Axis;
tempNeed.theLabel = kISpElementLabel_Axis_Yaw;
tempNeed.flags = kISpNeedFlag_Axis_AlreadyButton;
myNeeds[kNeed_Yaw] = tempNeed;

GetIndString (tempNeed.name, kSTRn_NeedNames,