OUYA support

Personally, I just used the Ouya library provided and scheduled a timer to update a controller abstraction in my game library using JNI.

Tyler Hennely wrote:

Personally, I just used the Ouya library provided and scheduled a timer to update a controller abstraction in my game library using JNI.

Would you mind sharing how you did that? I only have rudimentary knowledge of Android development and JNI

*EDIT![](:* Stever Tranby above seems to have a much better solution than what I have demonstrated here. I haven’t tested it out, but he’s piping input events much more directly into OpenGL. My approach resulted in a lot of needless rerouting, I expect. /Edit

Sure) I don’t have the source in front of me, I’m afraid, but I can give the major plot points.

In the onCreate function of your main android activity, you’ll want to schedule a Java Timer subclass, that will call a function ever N milliseconds. I used 50 ms, but my game didn’t need to be super-sensitive with regard to input, either.

What the timed function does is check in with the Ouya controller API for the state of all buttons. You call something like OuyaController.getController(1).getButton(OuyaController.BUTTON_O), which returns the setting of a flag for the O button. There are float x,y coordinates for the joysticks.

The API has changed quite a lot. For example, I believe there’s now a way to have the API alert your activity, rather than you polling the API, but it will make little difference which method you use in the next step.

So, at this point, your Java code knows the state of the controller. To forward that information into your Cocos2d C*+ library, you need to set up another JNI call.
In your Java activity, you declare a function called:
public native int updateController;
.
Then, in C*+ library, you add a function with a very specific declaration. For the function I’ve just defined, it would need to look like this:

JNIEXPORT int JNICALL Java_com_(company)_(product)_ProjectName_updateController( JNIEnv * env, jobject obj, jint playerNumber,
jint faceUp,
jint faceDown,
jint faceRight,
jint faceLeft,
jint dPadDown,
jint dPadUp,
jint dPadRight,
jint dPadLeft,
jint l1, jint l2,
jint r1, jint r2,
jint home
);

‘com_Company_Product’ is the package name of the Java file containing your main activity. If the package were com.apple.iOS, that section of the function name would become Java_com_apple_iOS_AngryBirds_updateController( …

What you do with the information from that point is up to you. In my case, I had a controller class in the C++ library that just listened for that incoming information and stored it. In each scene’s update function, I checked the state of that controller abstraction and forwarded it to a unified input handling system whose job was to forward input commands from controllers and keyboard to other parts of the game. I can’t call anything I did after the JNI stuff good design, though. I’d recommend you figure out a solution that works best for your designs.

Hi,

yeah I had something similar to Steve implemented previously. The current version of the v3.0 branch of cocos2d-x does not have any java source for the application, instead it uses NativeActivity and the application main is all in c++ code.

The main script for android lives here:
https://github.com/cocos2d/cocos2d-x/blob/develop/cocos2dx/platform/android/nativeactivity.cpp

Right now, to see things working, I am just trying to hook into handle_key_input()
https://github.com/cocos2d/cocos2d-x/blob/develop/cocos2dx/platform/android/nativeactivity.cpp#L421

I am trying to call the OuyaBindController (pulled from the MetaProject project, https://github.com/levire/MetaProject)

//OuyaBindController.java:onKeyDown(final int pKeyCode, final KeyEvent pKeyEvent)
cocos2d::JniMethodInfo ccxouyaKeyHandler;
cocos2d::JniHelper::getStaticMethodInfo(ccxouyaKeyHandler,
                                             "com/levire/ouyabind/OuyaBindController",
                                             "onKeyDown",
                                             "(ILandroid/view/KeyEvent;)Z");
jboolean ouyaHandled = ccxouyaKeyHandler.env->CallStaticBooleanMethod(
                                        ccxouyaKeyHandler.classID,
                                        ccxouyaKeyHandler.methodID,
                                        (int)AKeyEvent_getKeyCode(event), event);

It crashes immediately upong trying to press a controller button though,

W/dalvikvm( 1727): dvmFindClassByName rejecting 'com/levire/ouyabind/OuyaBindController'
W/dalvikvm( 1727): Invalid indirect reference 0x420422d8 in decodeIndirectRef
E/dalvikvm( 1727): VM aborting
F/libc    ( 1727): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1), thread 1740 (com.MyGame)
I/DEBUG   (  110): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (  110): Build fingerprint: 'OUYA/ouya_1_1/ouya_1_1:4.1.2/JZO54L-OUYA/519:user/test-keys'

Like I said, I only have rudimentary Android/JNI knowledge, so I am still trying to work through this.

I am still unable to solve my issue. The app loads fine on Ouya, but it crashes when I try to handle controller input. As shown above I am simply trying to hook into the handle_key_input() function.

Does anyone have any suggestions as to how to debug this crash? It crashes at the CallStaticBooleanMethod call. I don’t believe it is an issue finding the class, as I see the same dvmFindClassByName error for the Cocos2dxHelper class which is used elsewhere in the same file, i.e.) dvmFindClassByName rejecting ‘org/cocos2dx/lib/Cocos2dxHelper’

In previous revs of cocos2dx that used Cocos2dxGLSurfaceView.java, ouya controller input worked by just adding some custom code to the onKeyDown/onKeyUp methods, e.g.)

public boolean onKeyDown(final int pKeyCode, final KeyEvent pKeyEvent) {
    OuyaBindController.onKeyDown(pKeyCode, pKeyEvent);

Thanks for any help on this

Ok,

I’ve got it working with cocos2d-x version 3.0 develop branch and NativeActivity.

If anyone else is interested, I’ve based it off the levire.com Metaproject (https://github.com/levire/MetaProject) with the following modifications:

OuyaBindController.java:

// added the init method
public static void init(final Activity activity) {
    Log.d("OUYA Controller", "init called");
    OuyaController.init(activity);
}
// added onKeyDown/Up methods that do not require KeyEvent
public static boolean onKeyDownByDeviceId(final int pKeyCode, final int deviceId) {
    OuyaBindController.onNativeKeyDown(pKeyCode, deviceId);
    return true;
}
public static boolean onKeyUpByDeviceId(final int pKeyCode, final int deviceId) {
    OuyaBindController.onNativeKeyUp(pKeyCode, deviceId);
    return true;
}

in nativeactivity.cpp

// add a call to OuyaBindController::init() 
static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
    // ...
    case APP_CMD_INIT_WINDOW:
        // ...
        // initialize the ouya sdk OuyaController class with our Activity
        cocos2d::JniMethodInfo ccxouyaInit;
        if (!cocos2d::JniHelper::getStaticMethodInfo(
                ccxouyaInit,
                "com/levire/ouyabind/OuyaBindController",
                "init",
                "(Landroid/app/Activity;)V")) {
            LOGI("cocos2d::JniHelper::getStaticMethodInfo(ccxouyaInit) FAILED");
        }
        ccxouyaInit.env->CallStaticVoidMethod(ccxouyaInit.classID,
                                              ccxouyaInit.methodID,
                                              app->activity->clazz);
// ...
// in handle_key_input() 
static int32_t handle_key_input(AInputEvent *event)
{
    if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP)
    {

        cocos2d::JniMethodInfo ccxouyaKeyHandler;
        if (!cocos2d::JniHelper::getStaticMethodInfo(
                ccxouyaKeyHandler,
                "com/levire/ouyabind/OuyaBindController",
                "onKeyUpByDeviceId",
                "(II)Z")) {
            LOGI("cocos2d::JniHelper::getStaticMethodInfo(ccxouyaKeyHandler) FAILED");
         }
         jboolean ouyaHandled = ccxouyaKeyHandler.env->CallStaticBooleanMethod(
                                    ccxouyaKeyHandler.classID,
                                    ccxouyaKeyHandler.methodID,
                                    (int)AKeyEvent_getKeyCode(event), (int)AInputEvent_getDeviceId(event));

        switch (AKeyEvent_getKeyCode(event))
        {
        case AKEYCODE_BACK:
            {
                cocos2d::EventKeyboard event(cocos2d::EventKeyboard::KeyCode::KEY_BACKSPACE, false);
                cocos2d::EventDispatcher::getInstance()->dispatchEvent(&event);
            }
            return 1;
        case AKEYCODE_MENU:
            {
                cocos2d::EventKeyboard event(cocos2d::EventKeyboard::KeyCode::KEY_MENU, false);
                cocos2d::EventDispatcher::getInstance()->dispatchEvent(&event);
            }
            return 1;
        default:
            break;
        }
    }
    else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) {
        cocos2d::JniMethodInfo ccxouyaKeyHandler;
        if (!cocos2d::JniHelper::getStaticMethodInfo(
                ccxouyaKeyHandler,
                "com/levire/ouyabind/OuyaBindController",
                "onKeyDownByDeviceId",
                "(II)Z")) {
            LOGI("cocos2d::JniHelper::getStaticMethodInfo(ccxouyaKeyHandler) FAILED");
        }
        jboolean ouyaHandled = ccxouyaKeyHandler.env->CallStaticBooleanMethod(
                                        ccxouyaKeyHandler.classID,
                                        ccxouyaKeyHandler.methodID,
                                        (int)AKeyEvent_getKeyCode(event), (int)AInputEvent_getDeviceId(event));

Mike M wrote:

Ok,
>
I’ve got it working with cocos2d-x version 3.0 develop branch and NativeActivity.
>
If anyone else is interested, I’ve based it off the levire.com Metaproject (https://github.com/levire/MetaProject) with the following modifications:
>
OuyaBindController.java:
[…]
>
in nativeactivity.cpp
[…]

Mike,

Are there any obvious caveats (functionality or performance issues) that you are experiencing with your Ouya implementation?

Just curious about known issues before considering porting a rather large app).

Thanks.