0

Using a physical Joystick in a Cocos2D (for Mac) Game (Part 1)

Quite a while ago I started playing around with Cocos2D for Mac and so far I love it. As a first test project I am making a retro style side-scrolling space shoot-em’-up.
While processing keyboard input is quite easy, the only thing I was really missing is built-in support for joysticks and gamepads. I could not find anything related to this topic on the official Cocos2D forums. You only get plenty of articles describing how to implement a soft-gamepad on the iPhone or iPad. I guess this is because the Mac port of Cocos2D is quite new.

After doing some research I found out that a joystick is a HID (Human Interface Device) and there is a crude, low level C API on the Mac to interface HIDs called IOHIDLib. Then I found out about Dave Dribin’s excellent DDHidLib library, an Objective-C wrapper around IOHIDLib. You can download it here: DDHidLib. In the first part of the tutorial I’ll show you how to integrate it in your Cocos2D project (I assume you use Xcode 4).

Building the Framework

  1. Download and extract the source code of DDHidLib from the above link.
  2. Open the DDHidLib Xcode project.
  3. Select the DDHidLib scheme.
  4. Edit the scheme so it uses the Release build configuration.
  5. Build the framework. If you run into problems here make sure the Base SDK is set to “Latest Mac OS X” in the target’s build settings.
  6. Locate the framework in Finder for later use.

Demo Project

Now I’ll take you through the process of integrating DDHidLib into a Cocos2D project.
Create a new project in Xcode using the template called “cocos2d_macosx” and name it “JoystickTest”. Build and run. This should just display a Hello World label. Now to the fun part.

Adding the Framework

  1. Drag DDHidLib.framework into the Frameworks group of your demo project and make sure “Copy items” is checked. The framework will now be linked to the target.
  2. Go to you project settings, select the JoystickTest target.
  3. Select the “Build Phases” tab and add a new “Copy Files” phase.
  4. Select destination “Frameworks” and also rename the phase to “Frameworks”.
  5. Drag DDHidLib.framework from the project navigator into the build phase.
  6. Build and run the project to see if everything works.

Intercepting Joystick Events

  1. Open HelloWorldLayer.h and add a member variable:
    @interface HelloWorldLayer : CCLayer
    {
        NSArray *joysticks;
    }
  2. Open HelloWorldLayer.m and add these pieces:

    #import <DDHidLib/DDHidLib.h>

    At the end of the init method add

    // allJoysticks returns an array of all connected joysticks
    joysticks = [[DDHidJoystick allJoysticks] retain];

    // we want to be the delegate of the joysticks
    [joysticks makeObjectsPerformSelector:@selector(setDelegate:) withObject:self];

    // start listening to all joystick events
    [joysticks makeObjectsPerformSelector:@selector(startListening) withObject:nil];
  3. Now we can receive delegate methods which are called when the user interacts with the joystick.
    Add the following code at the end of the file. Use these delegate methods to set states or trigger actions in your game logic. The methods are pretty self explanatory.

    #pragma mark - Joystick stuff

    - (void)ddhidJoystick:(DDHidJoystick *)joystick buttonDown:(unsigned)buttonNumber
    {
        // this method would be a good place to start shooting bullets
        NSLog(@"button down: %d", buttonNumber);
    }

    - (void)ddhidJoystick:(DDHidJoystick *)joystick buttonUp:(unsigned)buttonNumber
    {
        // this method would be a good place to stop shooting bullets
        NSLog(@"button up: %d", buttonNumber);
    }


    #define THRESHOLD   2<<13   // this threshold defines the dead zone of analog sticks

    - (void)ddhidJoystick:(DDHidJoystick *)joystick
                    stick:(unsigned)stick
                 xChanged:(int)value
    {
        // this method gets called when an analog stick is moved on the x axis.
       
        if(value < -THRESHOLD)
            NSLog(@"stick left");
        else if(value > THRESHOLD)
            NSLog(@"stick right");
        else
            NSLog(@"x axis neutral zone");
    }

    - (void)ddhidJoystick:(DDHidJoystick *)joystick
                    stick:(unsigned)stick
                yChanged:(int)value
    {
        // this method gets called when an analog stick is moved on the y axis.
       
        if(value < -THRESHOLD)
            NSLog(@"stick up");
        else if(value > THRESHOLD)
            NSLog(@"stick down");
        else
            NSLog(@"y axis neutral zone");
    }

    - (void)ddhidJoystick:(DDHidJoystick *)joystick
                    stick:(unsigned)stick
                otherAxis:(unsigned)otherAxis
             valueChanged:(int)value
    {
        NSLog(@"otherAxis: %u   value: %d", otherAxis, value);
    }

    - (void)ddhidJoystick:(DDHidJoystick *)joystick
                    stick:(unsigned)stick
                povNumber:(unsigned)povNumber
             valueChanged:(int)value
    {
        // This method gets called when d-pad buttons are pressed. The value is direction in degrees * 100
       
        switch (value)
        {
            case -1:
                text = @"d-pad none";
                break;
               
            case 0:
                text = @"d-pad N";
                break;
           
            case 4500:
                text = @"d-pad NE";
                break;
               
            case 9000:
                text = @"d-pad E";
                break;

            case 13500:
                text = @"d-pad SE";
                break;

            case 18000:
                text = @"d-pad S";
                break;

            case 22500:
                text = @"d-pad SW";
                break;

            case 27000:
                text = @"d-pad W";
                break;
               
            case 31500:
                text = @"d-pad NW";
                break;
               
            default:
                break;
        }
    }
  4. Connect a joystick or pad to your Mac and try it out!

You can download the finished Xcode project here: JoystickTest.zip