Adding iCade Support to Your Game

This is a blog post by iOS Tutorial Team member Jacob Gundersen, an indie game developer who runs the Indie Ambitions blog. Check out his latest app – Factor Samurai! The iCade is a miniature arcade cabinet for your iPad. It communicates with the iPad over Bluetooth, and allows you to play iCade-compatible games with […] By Jake Gundersen.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Implementing the ControlsDelegate Protocol

Now you’re going to set up the delegate on the ActionLayer. First you need to set the delegate in initWithHud. Change that method to the following:

- (id)initWithHUD:(HUDLayer *)hud
{
    if ((self = [super init])) {
        _hud = hud;
        _hud.delegate = self;  //here's our new line
        // Rest of method...
        [self setupWorld];
        [self setupLevelHelper];
        [self setupDebugDraw];
        [self setupAudio];
        [self scheduleUpdate];
        //self.isTouchEnabled = YES;
        _lives = 3;
        [self updateLives];
    }
    return self;
}

When you do this, you’ll immediately get an error telling you that the ActionLayer class doesn’t implement the ControlsDelegate. Switch to the header and change the interface line:

@interface ActionLayer : CCLayer <ControlsDelegate> {

That takes care of the error. Now implement the methods. There are two:

#pragma mark ControlsDelegate methods

-(void)heroMove:(ControlDirection)direction {
    if (direction == kDirectionLeft) {
        _playerVelX = -MOVE_POINTS_PER_SECOND;
        _hero.flipX = YES;
    } else if (direction == kDirectionRight) {
        _playerVelX = MOVE_POINTS_PER_SECOND;
        _hero.flipX = NO;
    } else {
        _playerVelX = 0;
    }
}

-(void)heroJump {
    _heroBody->ApplyLinearImpulse(b2Vec2(_playerVelX/[_lhelper pixelsToMeterRatio], 1.25), _heroBody->GetWorldCenter());
    [[SimpleAudioEngine sharedEngine] playEffect:@"wing.wav"];
    [_lhelper startAnimationWithUniqueName:@"Flap" onSprite:_hero];
}

This code should look familiar if you did the HUDLayer tutorial that it comes from, but if you didn’t, I’ll explain.

The first method sets the _playerVelX property. In updateHero: this property is used to set the hero’s x velocity. He’ll move that distance each frame. This code also changes the hero’s sprite so that he’s facing the direction he’s moving. If a button is released, then the hero’s x velocity is set to 0 (don’t move).

The jump method applies a linear impulse, pushing the hero up. It plays a wing-flapping sound and turns on a wing-flapping animation. The player can release and tap this button to fly around.

Build and run now – you should be able to fly around the level!

Can We Do iCade Now!!?

Now, finally on to the iCade, the hardest part. Just kidding – this is actually the easiest part (again, thanks to Stuart Carnie). You’re going to use the iCadeEventDelegate methods to interact with the iCadeReaderView.

In HUDLayer.h, import iCadeReaderView.h, and set the HUDLayer as implementing the iCadeEventDelegate protocol:

#import "iCadeReaderView.h"

@interface HUDLayer : CCLayer <iCadeEventDelegate> {

Then implement these two methods from the delegate protocol in HUDLayer.mm:

-(void)buttonDown:(iCadeState)button {
    if (button == iCadeJoystickLeft) {
        [delegate heroMove:kDirectionLeft];
        leftButton.opacity = 255;
    } else if (button == iCadeJoystickRight) {
        [delegate heroMove:kDirectionRight];
        rightButton.opacity = 255;
    } else if (button == iCadeButtonA || 
               button == iCadeButtonB || 
               button == iCadeButtonC || 
               button == iCadeButtonD || 
               button == iCadeButtonE || 
               button == iCadeButtonF || 
               button == iCadeButtonG || 
               button == iCadeButtonH) {
        [delegate heroJump];
        jumpButton.opacity = 255;
    } 
}

-(void)buttonUp:(iCadeState)button {
    if (button == iCadeJoystickLeft) {
        [delegate heroMove:kDirectionNone];
        leftButton.opacity = 127;
    } else if (button == iCadeJoystickRight) {
        [delegate heroMove:kDirectionNone];
        rightButton.opacity = 127;
    } else if (button == iCadeButtonA || 
               button == iCadeButtonB || 
               button == iCadeButtonC || 
               button == iCadeButtonD || 
               button == iCadeButtonE || 
               button == iCadeButtonF || 
               button == iCadeButtonG || 
               button == iCadeButtonH) {
        jumpButton.opacity = 127;
    } 
}

Each time a button is pressed, the buttonDown method is called and passes the button (a bitmask) as a parameter. Each time a button is released, buttonUp is called.

You’re just testing to see which button has been pressed or released, and then implementing very similar logic to what you did with your onscreen buttons. You change the opacity of the button sprites and send the appropriate method to the delegate.

As you can see, any pressed button will send the jump message.

Now you need to create the iCadeReaderView object, set the delegate, and add it to the view hierarchy. Add the following code to the end of init, still within the main if statement:

	iCadeReaderView *icrv = [[iCadeReaderView alloc] initWithFrame:CGRectZero];
        EAGLView *root = [[CCDirector sharedDirector] openGLView];
        [root addSubview:icrv];
        icrv.active = YES;
        icrv.delegate = self;

Here you’re setting up the view with no frame, so it’s invisible. You then send a reference to EAGLView so you can insert your iCadeReaderView into the view hierarchy. Then set it to active – this is required for it to work. Finally, you set your HUDLayer as the delegate of the iCadeEventDelegate protocol methods.

Guess what? That’s it! Really, really easy. Fire up your iCade now, make sure it’s paired as you did before and that the Bluetooth is connected, and you can control your flying cupid kid in retro miniature arcade bliss!

Where to Go From Here?

Our game running on the iCade!

Our game running on the iCade!

Here is the finished iCade game we developed in this tutorial.

If you don’t have an iCade, you should get one. Look at it this way: if you develop a game that supports it, it will be on a relatively short list of games that do. That means more visibility and more sales for your game.

There, I’ve given you a sound business reason to go out and buy an iCade. You’re welcome.

Once you have your iCade you must, must play Super Mega Worm and Mos Speedrun. You will thank me. Also, if you were lucky enough to score iMAME before it was pulled, that also supports the iCade.

It will be fun to see if the iCade and related devices can make more inroads and gain greater support among developers. With a wireless Bluetooth controller that supported iCade and a HDMI connection, you could use your phone as a home gaming console! Hey, it could happen.

Hope to hear from you in the forums!

Note from Ray: Want more iCade game recommendations? I asked you guys via Twitter a while back, and here were the top recommendations: Velocispider, League of Evil, Temple Run, and Caverns of Minos. Enjoy! :]


This is a blog post by iOS Tutorial Team member Jacob Gundersen, an indie game developer and co-founder of Third Rail Games. Check out his latest app – Factor Samurai!

Jake Gundersen

Contributors

Jake Gundersen

Author

Over 300 content creators. Join our team.