Previous Book Contents Book Index Next

Inside Macintosh: Apple Game Sprockets Guide /
Chapter 3 - InputSprocket


Using InputSprocket

This section illustrates basic ways of using InputSprocket. In particular, it provides source code samples that show how you can

Note
The code samples shown in this section provide no error handling.

Initializing Need Structures and Allocating Virtual Elements

During initialization the game describes its requirements for input by filling out a ISpNeed data structure for each requirement (the structure is described on (page 3-29). The need structure has fields for an element kind and an element label, so the game can describe the data and its intended use. Other fields specify how to use the keyboard and mouse for keyboard emulation. The game also provides a string and an icon to identify the input requirement in a user interface.

After filling out the need structures, the game allocates a virtual element to meet each input requirement. Allocation of virtual elements must happen before you call ISpInit because after that call the device driver may begin using the virtual elements.

The InitNeeds routine shown in Listing 3-1 creates an array of initialized ISpNeed structures. It then uses the ISpElement_NewVirtualFromNeeds function (page 3-36) to allocate virtual elements. You can use ISpElement_NewVirtualFromNeeds only with built-in element kinds, which is the case in the example. Otherwise, you allocate virtual elements individually using the ISpElement_NewVirtual function (page 3-36).

Listing 3-1 Initializing need structures and allocating virtual elements

OSStatus InitNeeds(ISpNeed *theNeeds,ISpElementReference *elements)
{
   ISpNeed tempNeeds[kNumNeeds] =
   {
      { 
         "\pForward Thrust", 
         kIconSuiteID_YThrust, 
         kISpElementKind_Axis,
         kISpElementLabel_YAxis, 
         0
      },
      { 
         "\pSide Thrust", 
         kISpElementKind_Axis,
         kISpElementLabel_XAxis,
         0
      },
      { 
         "\pVertical Thrust", 
         kIconSuiteID_ZThrust,
         kISpElementKind_Axis, 
         kISpElementLabel_ZAxis, 
         0
      },
      { 
         "\pLook", 
         kIconSuiteID_Look, 
         kISpElementKind_Movement,
         kISpElementLabel_None,  
         0
      },
      { 
         "\pLaser", 
         kIconSuiteID_Fire, 
         kISpElementKind_Button,
         kISpElementLabel_Fire, 
         0
      },
      { 
         "\pMissle", 
         kIconSuiteID_Pause, 
         kISpElementKind_Button,
         kISpElementLabel_Fire, 
         0
      },
      { "\pShields", 
         kIconSuiteID_Shields,
         kISpElementKind_Button,
         kISpElementLabel_None, 
         0
      },
      { 
         "\pStart/Stop", 
         kIconSuiteID_Start, 
         kISpElementKind_Button,
         kISpElementLabel_Start, 
         kISpNeedFlag_NoMultiConfig
      }
   };

   int itr;
   for(itr = 0; itr < kNumNeeds; itr++)
   {
      theNeeds[itr] = tempNeeds[itr];
   }

   OSStatus result = ISpElement_NewVirtualFromNeeds(kNumNeeds, theNeeds,
      elements, 0);

   return result;
}

Building an Element List

When configuration is finished and virtual elements are allocated, you probably want to build a list of elements the game wants to get events for during play. Listing 3-2 shows the BuildMyElementList routine, which puts all the button and directional pad elements on an element list. Games usually poll axis and movement elements, so they are not included on the list.

BuildMyElementList also makes the reference constant returned by ISpElementList_AddElements for each element it adds equal to the index of the element in the array of need structures passed to the drivers during autoconfiguration. This is also the same as the index of the element in the list of virtual elements allocated during the autoconfiguration process (see Listing 3-1 for an example of creating virtual elements). The game uses the reference constant (which is passed in the ISpElementEvent structure) to identify the element when it gets element events using ISpElementList_GetNextEvent.

Listing 3-2 Building an element list

ISpElementListReference BuildMyElementList(UInt32 count, ISpNeed *needs, 
   ISpElementReference *elements)
{
   int itr;
   ISpElementListReference theList = nil;

   ISpElementList_New(0, nil, &theList, 0);
   for(itr = 0; itr < count; itr++)
   {
      if ((needs[itr].theKind == kISpElementKind_Button) ||
         (needs[itr].theKind == kISpElementKind_DPad))
      {
            ISpElementList_AddElements(theList, itr, 1,
               &(elements[itr]));
      }
   }

   return theList;
}
BuildMyElementList first defines an element list reference to the list that will be built. It next uses the ISpElementList_New function (page 3-51) to create an empty element list. Finally, BuildMyElementList uses the ISpElementList_AddElements function (page 3-53) to add elements of kind kISpElementKind_Button and kISpElementKind_DPad to the list. They are added one at a time so that each element can be assigned the reference constant that corresponds to its index in the array of need structures.

Processing Input Data During Play

During game play the game obtains data from all the input devices either by polling or getting events. Listing 3-3 shows the ProcessInput routine. This routine runs the main game loop, polling for the state of axis and movement elements and getting events from button elements.

ProcessInput takes an element list that has been built so that the index of each element corresponds to its position in the array of need structures passed to the drivers during autoconfiguration (see Listing 3-2 for an example of how to do this).

Notice that there are two routines for getting the state of an element--ISpElement_GetSimpleState and ISpElement_GetComplexState. The ISpElement_GetSimpleState function (page 3-47) is for elements whose data fits in an unsigned 32-bit integer. For other elements, in this case, movement kind elements, use ISpElement_GetComplexState (page 3-47) where you can specify the size of the buffer needed to hold the data.

Buttons work in a variety of ways and the code illustrates how to get events for three types--a firing button, where only down events matter (the laser and missile buttons); a toggle (the stop button); and a hold-to-activate button (the shield button).

Listing 3-3 Processing input data

enum
{
   kForwardThrust = 0,
   kSideThrust,
   kVerticalThrust,
   kLook,
   kLaser,
   kMissle,
   kShields,
   kStartStop,
   kNumNeeds
};

typedef struct InputData
{
   UInt32   x,y,z;       // x,y and z thrust
   UInt32   xLook, yLook;
   Boolean  fireLasers;
   Boolean  fireMissles;
   Boolean  shields;    // down activates; up deactivates
   Boolean  stopped;    // toggle
} InputData;
   
void ProcessInput(ISpElementReference *theElements, 
   ISpElementListReference myList, InputData *myInput)
{
   ISpElement_GetSimpleState(theElements[kForwardThrust],
       &(myInput->x));
   ISpElement_GetSimpleState(theElements[kSideThrust], &(myInput->y));
   ISpElement_GetSimpleState(theElements[kVerticalThrust],
      &(myInput->z));
      
   ISpMovementData tempMovement;
   ISpElement_GetComplexState(theElements[kLook],
      sizeof(ISpMovementData),&tempMovement);
   myInput->xLook = tempMovement.xAxis;
   myInput->yLook = tempMovement.yAxis;

   myInput->fireLasers = false;
   myInput->fireMissles = false;
    
   Boolean wasEvent;
   ISpElementEvent theEvent;
    
   while(1)
   {
      ISpElementList_GetNextEvent(myList, sizeof(ISpElementEvent),
         &theEvent, &wasEvent);
             
      switch(theEvent.refCon)
      {
         case kLaser:
            if (theEvent.data == kISpButtonDown)
            {
               myInput->fireLasers = true;
            }
         break;
                       
         case kMissle:
            if (theEvent.data == kISpButtonDown)
            {
               myInput->fireMissles = true;
            }
         break;
                       
         case kShields:
            if (theEvent.data == kISpButtonDown)
            {
               myInput->shields = true;
            }
            else if (theEvent.data == kISpButtonUp)
            {
               myInput->shields = false;
            }
         break;
                      
         case kStartStop:
            if (theEvent.data == kISpButtonDown)
            {
               myInput->stopped = !myInput->stopped;
            }
         break;
      }
           
      if (!wasEvent)
      {
         return;
      }
   }
}

Turning the Keyboard and Mouse On and Off

By default, keyboard and mouse input devices are inactive. This is fine for games that run in a window where the user probably expects the mouse and keyboard to behave normally. However, most games will want to activate the keyboard and mouse. Later, there may be a point in the game where the user needs to enter text, in which case, you would want to deactivate the keyboard and mouse as game input devices so that you can return to using the standard methods provided by the OS to manage them.

Listing 3-4 shows the SetKeyboardMouseActivation function that deactivates and activates the keyboard and mouse devices.

Listing 3-4 Turning off keyboard and mouse devices


void SetKeyboardMouseActivation(Boolean active)
{
   enum           { kSimple = 100 }; 
   ISpDeviceReference buffer[kSimple];
   UInt32         count;

   ISpDevices_ExtractByClass(kISpDeviceClass_Mouse, kSimple, &count,
      buffer);
             
   if (active) { ISpDevices_Activate(count,buffer); }
   else { ISpDevices_Deactivate(count, buffer); }
     
   ISpDevices_ExtractByClass(kISpDeviceClass_Keyboard,kSimple,&count,
      buffer);

   if (active) { ISpDevices_Activate(count,buffer); }
   else { ISpDevices_Deactivate(count, buffer); }
}
The SetKeyboardMouseActivation function uses ISpDevices_ExtractByClass (page 3-39) to find the keyboard and mouse devices. Notice that the buffer allocated to hold the device references is allocated for 100 devices, a number likely to be sufficient. The ISpDevices_Deactivate function is described on (page 3-41); ISpDevices_Activate is on (page 3-41).


Previous Book Contents Book Index Next

© Apple Computer, Inc.
2 JUL 1996