iCade Support

If you are building an app for iOS and want to include iCade support this is a little gem that I found the last night:

http://kode80.com/osx-apps/simul80/

The application lets you simulate an iCade controller in the simulator or you can even pair your device to your mac and use the keyboard as a controller on there. There is also a really nice feature that I managed to get working this morning which is get the xBox controller to play iCade supported games on iOS devices. It might also work for android, but I have not tired that.

It also supports GameDock if you are interested in that.

I have written some code that adds iCade support to iOS games via the existing cocos keypad code… If anyone is interested… just give me a shout and I would be happy to help.

Bringing back an old thread probably isnt the best first post for a newb on here BUT

I talked to the guy behind simul80 and right now it doesnt work by android (not by design or anything, just something screwy with android devices and how the app connects)

The reason i was looking in the first place was i wanted to find how to do iCade for both android and iOS, but have not yet found a way to do it for both platforms on cocos2d-x :frowning:
Still looking though

The code I have was ported from the iOS driver… It relies on adding some input field to the UIView… I think it should be easier on android… Don’t have access to the code right now… but will post it very shortly.

OK so I started with the 3 files in

https://github.com/scarnie/iCade-iOS/tree/master/iCadeTest/iCade

I put them under libs/iOS/iCade

Then I wrote a manager

#ifndef __iCadeManager_H__
#define __iCadeManager_H__

#include "cocos2d.h"
USING_NS_CC;

#include "iCadeMovementDelegate.h"

class iCadeManager : public CCObject {
public:
    virtual ~iCadeManager();
    static iCadeManager *sharediCadeManager();
    virtual bool init();
    void enableUsingScheduler(float dt);
    void enable();
    void disable();
    ccKeypadKeycode remapToKey(short state);

    CREATE_FUNC(iCadeManager);

};

#endif

#include "iCadeManager.h"

#import "../../../libs/ios/iCade/iCadeReaderView.h"
#import "EAGLView.h"
#import "../../../proj.ios/AppController.h"
#import "../../../proj.ios/RootViewController.h"
#import "../../../libs/cocos2dx/platform/ios/EAGLView.h"

#include "cocos2d.h"
USING_NS_CC;

static iCadeReaderView *_icrv = nil;

@interface iCadeReaderViewDelegateWrapper : NSObject  {
@public
    BOOL _isUpPressed;
    BOOL _isDownPressed;
    BOOL _isAnyButtonPressed;

    struct {
        BOOL moveUp = YES;
        BOOL moveDown = YES;
        BOOL stopMoving = YES;
        BOOL fireWeapon = YES;
        BOOL iCadeActive = YES;
    } _delegateFlags;
}
- (void)buttonDown:(iCadeState)button;
- (void)buttonUp:(iCadeState)button;
@end

@implementation iCadeReaderViewDelegateWrapper

#pragma mark - iCadeReaderView callbacks

-(void)buttonDown:(iCadeState)button {

    if (button == iCadeJoystickUp) {
        //        CCLOG(@"pressing up");
        _isUpPressed = TRUE;
    } else if (button == iCadeJoystickDown) {
        //        CCLOG(@"pressing down");
        _isDownPressed = TRUE;
    } else if (button == iCadeButtonA ||
               button == iCadeButtonB ||
               button == iCadeButtonC ||
               button == iCadeButtonD ||
               button == iCadeButtonE ||
               button == iCadeButtonF ||
               button == iCadeButtonG ||
               button == iCadeButtonH) {
        //        CCLOG(@"pressing button");
        _isAnyButtonPressed = TRUE;
    }

    if (button == iCadeJoystickUpRight) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickUp));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickRight));
    } else if (button == iCadeJoystickDownRight) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickDown));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickRight));
    } else if (button == iCadeJoystickUpLeft) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickUp));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickLeft));
    } else if (button == iCadeJoystickDownLeft) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickDown));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickLeft));
    } else {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyDown, iCadeManager::sharediCadeManager()->remapToKey(button));
    }
}

-(void)buttonUp:(iCadeState)button {

    if (button == iCadeJoystickUp) {
        //        CCLOG(@"released up");
        _isUpPressed = FALSE;
    } else if (button == iCadeJoystickDown) {
        //        CCLOG(@"released down");
        _isDownPressed = FALSE;
    } else if (button == iCadeButtonA ||
               button == iCadeButtonB ||
               button == iCadeButtonC ||
               button == iCadeButtonD ||
               button == iCadeButtonE ||
               button == iCadeButtonF ||
               button == iCadeButtonG ||
               button == iCadeButtonH) {
        //        CCLOG(@"released button");
        _isAnyButtonPressed = FALSE;
    }

    if (button == iCadeJoystickUpRight) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickUp));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickRight));
    } else if (button == iCadeJoystickDownRight) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickDown));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickRight));
    } else if (button == iCadeJoystickUpLeft) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickUp));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickLeft));
    } else if (button == iCadeJoystickDownLeft) {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickDown));
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(iCadeJoystickLeft));
    } else {
        CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeKeyUp, iCadeManager::sharediCadeManager()->remapToKey(button));
    }
}
@end

static iCadeManager *_sharediCadeManager = NULL;

iCadeManager *iCadeManager::sharediCadeManager() {

    if (_sharediCadeManager == nil) {
        _sharediCadeManager = iCadeManager::create();
        CC_SAFE_RETAIN(_sharediCadeManager);
    }

    return _sharediCadeManager;
}

bool iCadeManager::init() {

    // iCade
    _icrv = [[iCadeReaderView alloc] initWithFrame:CGRectZero];
    _icrv.active = NO;
    _icrv.delegate = [[iCadeReaderViewDelegateWrapper alloc] init];

    return true;
}

iCadeManager::~iCadeManager() {
    id temp = _icrv.delegate;
    _icrv.delegate = nil;
    [_icrv release];
    _icrv = nil;
    [temp release];
}

void iCadeManager::enableUsingScheduler(float dt) {
    enable();
}

void iCadeManager::enable() {
    EAGLView *root = [EAGLView sharedEGLView];

    // if we are adding a delegate when there was none before we need removed the keyboard listeners for the eagl view
    [[NSNotificationCenter defaultCenter] removeObserver:root
                                                    name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:root
                                                    name:UIKeyboardDidShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:root
                                                    name:UIKeyboardWillHideNotification object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:root
                                                    name:UIKeyboardDidHideNotification object:nil];


//    if (![[root subviews] containsObject:_icrv]) {
        [root addSubview:_icrv];
//    }

    _icrv.active = YES;
}

void iCadeManager::disable() {
    EAGLView *root = [EAGLView sharedEGLView];

    // if we are removing the delegate then we need to get keyboard events in the eagl view
    [[NSNotificationCenter defaultCenter] addObserver:root
                                             selector:@selector(onUIKeyboardNotification:)
                                                 name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:root
                                             selector:@selector(onUIKeyboardNotification:)
                                                 name:UIKeyboardDidShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:root
                                             selector:@selector(onUIKeyboardNotification:)
                                                 name:UIKeyboardWillHideNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:root
                                             selector:@selector(onUIKeyboardNotification:)
                                                 name:UIKeyboardDidHideNotification object:nil];
//    if ([[root subviews] containsObject:_icrv]) {
        [_icrv removeFromSuperview];
//    }

    _icrv.active = NO;
}

ccKeypadKeycode iCadeManager::remapToKey(short state) {
    ccKeypadKeycode key = kKeycodeUNKNOWN;
    switch (state) {
        case iCadeJoystickUp:
            key = kKeycodeDPAD_UP;
            break;
        case iCadeJoystickRight:
            key = kKeycodeDPAD_RIGHT;
            break;
        case iCadeJoystickDown:
            key = kKeycodeDPAD_DOWN;
            break;
        case iCadeJoystickLeft:
            key = kKeycodeDPAD_LEFT;
            break;
        case iCadeButtonA:
            key = kKeycodeBUTTON_A;
            break;
        case iCadeButtonB:
            key = kKeycodeBUTTON_B;
            break;
        case iCadeButtonC:
            key = kKeycodeBUTTON_C;
            break;
        case iCadeButtonD:
            key = kKeycodeBUTTON_X;
            break;
        case iCadeButtonE:
            key = kKeycodeBUTTON_Y;
            break;
        case iCadeButtonF:
            key = kKeycodeBUTTON_Z;
            break;
        case iCadeButtonG:
            key = kKeycodeBUTTON_SELECT;
            break;
        case iCadeButtonH:
            key = kKeycodeBUTTON_START;
            break;
        case iCadeJoystickNone:
        default:
            break;
    }

    return key;
}

So now there is one thing missing, and that is where do the constants e.g. kKeycodeBUTTON_SELECT come from. The answer is that they are an extenstion of the cocos2d-x enuration ccKeypadCode located in CCKeyPadDelegate.h

typedef enum {
    kKeycodeUNKNOWN = 0,
    kKeycodeSOFT_LEFT = 1,
    kKeycodeSOFT_RIGHT = 2,
    kKeycodeHOME = 3,
    kKeycodeBACK = 4,
    kKeycodeCALL = 5,
    kKeycodeENDCALL = 6,
    kKeycode0 = 7,
    kKeycode1 = 8,
    kKeycode2 = 9,
    kKeycode3 = 10,
    kKeycode4 = 11,
    kKeycode5 = 12,
    kKeycode6 = 13,
    kKeycode7 = 14,
    kKeycode8 = 15,
    kKeycode9 = 16,
    kKeycodeSTAR = 17,
    kKeycodePOUND = 18,
    kKeycodeDPAD_UP = 19,
    kKeycodeDPAD_DOWN = 20,
    kKeycodeDPAD_LEFT = 21,
    kKeycodeDPAD_RIGHT = 22,
    kKeycodeDPAD_CENTER = 23,
    kKeycodeVOLUME_UP = 24,
    kKeycodeVOLUME_DOWN = 25,
    kKeycodePOWER = 26,
    kKeycodeCAMERA = 27,
    kKeycodeCLEAR = 28,
    kKeycodeA = 29,
    kKeycodeB = 30,
    kKeycodeC = 31,
    kKeycodeD = 32,
    kKeycodeE = 33,
    kKeycodeF = 34,
    kKeycodeG = 35,
    kKeycodeH = 36,
    kKeycodeI = 37,
    kKeycodeJ = 38,
    kKeycodeK = 39,
    kKeycodeL = 40,
    kKeycodeM = 41,
    kKeycodeN = 42,
    kKeycodeO = 43,
    kKeycodeP = 44,
    kKeycodeQ = 45,
    kKeycodeR = 46,
    kKeycodeS = 47,
    kKeycodeT = 48,
    kKeycodeU = 49,
    kKeycodeV = 50,
    kKeycodeW = 51,
    kKeycodeX = 52,
    kKeycodeY = 53,
    kKeycodeZ = 54,
    kKeycodeCOMMA = 55,
    kKeycodePERIOD = 56,
    kKeycodeALT_LEFT = 57,
    kKeycodeALT_RIGHT = 58,
    kKeycodeSHIFT_LEFT = 59,
    kKeycodeSHIFT_RIGHT = 60,
    kKeycodeTAB = 61,
    kKeycodeSPACE = 62,
    kKeycodeSYM = 63,
    kKeycodeEXPLORER = 64,
    kKeycodeENVELOPE = 65,
    kKeycodeENTER = 66,
    kKeycodeDEL = 67,
    kKeycodeGRAVE = 68,
    kKeycodeMINUS = 69,
    kKeycodeEQUALS = 70,
    kKeycodeLEFT_BRACKET = 71,
    kKeycodeRIGHT_BRACKET = 72,
    kKeycodeBACKSLASH = 73,
    kKeycodeSEMICOLON = 74,
    kKeycodeAPOSTROPHE = 75,
    kKeycodeSLASH = 76,
    kKeycodeAT = 77,
    kKeycodeNUM = 78,
    kKeycodeHEADSETHOOK = 79,
    kKeycodeFOCUS = 80,
    kKeycodePLUS = 81,
    kKeycodeMENU = 82,
    kKeycodeNOTIFICATION = 83,
    kKeycodeSEARCH = 84,
    kKeycodeMEDIA_PLAY_PAUSE = 85,
    kKeycodeMEDIA_STOP = 86,
    kKeycodeMEDIA_NEXT = 87,
    kKeycodeMEDIA_PREVIOUS = 88,
    kKeycodeMEDIA_REWIND = 89,
    kKeycodeMEDIA_FAST_FORWARD = 90,
    kKeycodeMUTE = 91,
    kKeycodePAGE_UP = 92,
    kKeycodePAGE_DOWN = 93,
    kKeycodePICTSYMBOLS = 94,
    kKeycodeSWITCH_CHARSET = 95,
    kKeycodeBUTTON_A = 96,
    kKeycodeBUTTON_B = 97,
    kKeycodeBUTTON_C = 98,
    kKeycodeBUTTON_X = 99,
    kKeycodeBUTTON_Y = 100,
    kKeycodeBUTTON_Z = 101,
    kKeycodeBUTTON_L1 = 102,
    kKeycodeBUTTON_R1 = 103,
    kKeycodeBUTTON_L2 = 104,
    kKeycodeBUTTON_R2 = 105,
    kKeycodeBUTTON_THUMBL = 106,
    kKeycodeBUTTON_THUMBR = 107,
    kKeycodeBUTTON_START = 108,
    kKeycodeBUTTON_SELECT = 109,
    kKeycodeBUTTON_MODE = 110,
    kKeycodeESCAPE = 111,
    kKeycodeFORWARD_DEL = 112,
    kKeycodeCTRL_LEFT = 113,
    kKeycodeCTRL_RIGHT = 114,
    kKeycodeCAPS_LOCK = 115,
    kKeycodeSCROLL_LOCK = 116,
    kKeycodeMETA_LEFT = 117,
    kKeycodeMETA_RIGHT = 118,
    kKeycodeFUNCTION = 119,
    kKeycodeSYSRQ = 120,
    kKeycodeBREAK = 121,
    kKeycodeMOVE_HOME = 122,
    kKeycodeMOVE_END = 123,
    kKeycodeINSERT = 124,
    kKeycodeFORWARD = 125,
    kKeycodeMEDIA_PLAY = 126,
    kKeycodeMEDIA_PAUSE = 127,
    kKeycodeMEDIA_CLOSE = 128,
    kKeycodeMEDIA_EJECT = 129,
    kKeycodeMEDIA_RECORD = 130,
    kKeycodeF1 = 131,
    kKeycodeF2 = 132,
    kKeycodeF3 = 133,
    kKeycodeF4 = 134,
    kKeycodeF5 = 135,
    kKeycodeF6 = 136,
    kKeycodeF7 = 137,
    kKeycodeF8 = 138,
    kKeycodeF9 = 139,
    kKeycodeF10 = 140,
    kKeycodeF11 = 141,
    kKeycodeF12 = 142,
    kKeycodeNUM_LOCK = 143,
    kKeycodeNUMPAD_0 = 144,
    kKeycodeNUMPAD_1 = 145,
    kKeycodeNUMPAD_2 = 146,
    kKeycodeNUMPAD_3 = 147,
    kKeycodeNUMPAD_4 = 148,
    kKeycodeNUMPAD_5 = 149,
    kKeycodeNUMPAD_6 = 150,
    kKeycodeNUMPAD_7 = 151,
    kKeycodeNUMPAD_8 = 152,
    kKeycodeNUMPAD_9 = 153,
    kKeycodeNUMPAD_DIVIDE = 154,
    kKeycodeNUMPAD_MULTIPLY = 155,
    kKeycodeNUMPAD_SUBTRACT = 156,
    kKeycodeNUMPAD_ADD = 157,
    kKeycodeNUMPAD_DOT = 158,
    kKeycodeNUMPAD_COMMA = 159,
    kKeycodeNUMPAD_ENTER = 160,
    kKeycodeNUMPAD_EQUALS = 161,
    kKeycodeNUMPAD_LEFT_PAREN = 162,
    kKeycodeNUMPAD_RIGHT_PAREN = 163,
    kKeycodeVOLUME_MUTE = 164,
    kKeycodeINFO = 165,
    kKeycodeCHANNEL_UP = 166,
    kKeycodeCHANNEL_DOWN = 167,
    kKeycodeZOOM_IN = 168,
    kKeycodeZOOM_OUT = 169,
    kKeycodeTV = 170,
    kKeycodeWINDOW = 171,
    kKeycodeGUIDE = 172,
    kKeycodeDVR = 173,
    kKeycodeBOOKMARK = 174,
    kKeycodeCAPTIONS = 175,
    kKeycodeSETTINGS = 176,
    kKeycodeTV_POWER = 177,
    kKeycodeTV_INPUT = 178,
    kKeycodeSTB_POWER = 179,
    kKeycodeSTB_INPUT = 180,
    kKeycodeAVR_POWER = 181,
    kKeycodeAVR_INPUT = 182,
    kKeycodePROG_RED = 183,
    kKeycodePROG_GREEN = 184,
    kKeycodePROG_YELLOW = 185,
    kKeycodePROG_BLUE = 186,
    kKeycodeAPP_SWITCH = 187,
    kKeycodeBUTTON_1 = 188,
    kKeycodeBUTTON_2 = 189,
    kKeycodeBUTTON_3 = 190,
    kKeycodeBUTTON_4 = 191,
    kKeycodeBUTTON_5 = 192,
    kKeycodeBUTTON_6 = 193,
    kKeycodeBUTTON_7 = 194,
    kKeycodeBUTTON_8 = 195,
    kKeycodeBUTTON_9 = 196,
    kKeycodeBUTTON_10 = 197,
    kKeycodeBUTTON_11 = 198,
    kKeycodeBUTTON_12 = 199,
    kKeycodeBUTTON_13 = 200,
    kKeycodeBUTTON_14 = 201,
    kKeycodeBUTTON_15 = 202,
    kKeycodeBUTTON_16 = 203,
    kKeycodeLANGUAGE_SWITCH = 204,
    kKeycodeMANNER_MODE = 205,
    kKeycode3D_MODE = 206,
    kKeycodeCONTACTS = 207,
    kKeycodeCALENDAR = 208,
    kKeycodeMUSIC = 209,
    kKeycodeCALCULATOR = 210,
    kKeycodeZENKAKU_HANKAKU = 211,
    kKeycodeEISU = 212,
    kKeycodeMUHENKAN = 213,
    kKeycodeHENKAN = 214,
    kKeycodeKATAKANA_HIRAGANA = 215,
    kKeycodeYEN = 216,
    kKeycodeRO = 217,
    kKeycodeKANA = 218,
    kKeycodeASSIST = 219,
} ccKeypadKeycode;

The driver as I undertand it uses a letter to trigger the down action and another for the key up action, so as long as you don’t use these keys for something else in the app, then the android driver should be very simple. Listen to all key ups, if you get the down letter call the dispatcher and ask it to dispatch the equivalent arrow key.

Key mapping are

/*
 UP ON,OFF  = w,e
 RT ON,OFF  = d,c
 DN ON,OFF  = x,z
 LT ON,OFF  = a,q
 A  ON,OFF  = y,t
 B  ON,OFF  = h,r
 C  ON,OFF  = u,f
 D  ON,OFF  = j,n
 E  ON,OFF  = i,m
 F  ON,OFF  = k,p
 G  ON,OFF  = o,g
 H  ON,OFF  = l,v
*/

If you want to include any of this code in your game, it is released under the MIT license.

Hope that helps.

About the android issue… I found this video (http://www.youtube.com/watch?v=yOkYlnUeVXc) a while ago so not sure what he is talking about i.e. whether this is something new?!

OO very nice, ill see if i can get this working, thanks

The issue is just with how simul80 does its job right now, a standard icade device should connect fine, he thinks he might be missing a few things for it to work right with android but doesnt have a device to test with so its kind of a stand still :stuck_out_tongue: