Observation with badly-implemented input

fluffyfluffy Posts: 2Member
It seems like a lot of games (especially ones based on Unity) seem to have a pretty bad setup for their input event handler.  Looking at the SDK, it looks like all controller events go through the same View.onMotionEvent() hook, meaning it's getting one event for every fine-grained change in the analog sticks' position.  The thing I've noticed in a lot of games is that wiggling the stick between frames causes a lot of motion events to pile up, which then take several seconds to replay - not very helpful for timing-sensitive games (i.e. most of them).  It also seems like in some of these games, holding the stick doesn't have much effect, either.

I suspect that these problems come from games trying to do too much in onMotionEvent(), and are possibly doing their OpenGL repaint from there; a much better approach is for onMotionEvent to simply record the new stick positions, and then drive the player motion and such from the renderer's onDrawFrame() handler (so that you're guaranteed to have one and only one position update per frame, and that it's the render rate that essentially polls the joystick positions rather than having joystick position updates attempt to push renders faster than the Ouya can handle it).

Is there any section of the developer site that tracks best-practice/common-pitfall issues like these? I really want to see Ouya games actually be playable and fun, instead of frustrating experiences that fall apart on real hardware.

Comments

  • unfoilunfoil Posts: 12Member
    I've noticed that 90% of the games have horrible controls especially the ones created in unity. I created my own Joystick library that makes the controls for my game much nicer. Paired with my Joystick Mouse library I wrote it makes the Android touch screen and the Ouya controller work flawlessly in my games so they will work with either input method.

    Sorry for the late response but I waited for the retail version to start development.
  • rumplestilzkenrumplestilzken Posts: 181Member
    Writing an input library as we speak, definitely the way to go, is seems like otherwise input comes out pretty badly.
  • Killa_MaakiKilla_Maaki Posts: 504Member
    I'm using an input lib for Unity that works everywhere (not just OUYA) so it does not rely on OUYA input events... so hopefully that means much better performance than most.
    You didn't remember the plot of the Doctor Who movie because there was none; Just a bunch of plot holes strung together.
  • rumplestilzkenrumplestilzken Posts: 181Member
    I'm using an input lib for Unity that works everywhere (not just OUYA) so it does not rely on OUYA input events... so hopefully that means much better performance than most.
    Hopefully!! 

    Are you talking about not using android events or Ouya specific events? I haven't worked with the ODK almost at all yet, working on building a set of reusable libraries first. Then game specific dev, then ill add the ouya specific parts later, likely through JNI calls.

    As for input events, i am going as native as i can with my libraries, so I'm working with an input framework based on usage with native_app_glue. It is working out pretty well so far. 
  • Killa_MaakiKilla_Maaki Posts: 504Member
    I'm using an input lib for Unity that works everywhere (not just OUYA) so it does not rely on OUYA input events... so hopefully that means much better performance than most.
    Hopefully!! 

    Are you talking about not using android events or Ouya specific events? I haven't worked with the ODK almost at all yet, working on building a set of reusable libraries first. Then game specific dev, then ill add the ouya specific parts later, likely through JNI calls.

    As for input events, i am going as native as i can with my libraries, so I'm working with an input framework based on usage with native_app_glue. It is working out pretty well so far. 
    To be honest I don't know how Unity implements their joysticks. But I haven't heard anything about input lag on other platforms besides OUYA, so it must be specifically caused by using OUYA input events rather than Unity's builtin Input system. The one I'm using now is a wrapper around the Unity engine Input class, basically it creates a ton of different axes, and then depending on which controller is plugged in, it figures out which axis maps to what physical part of the controller so that it can map perfectly between OUYA, Xbox, PS3, Moga, etc.
    You didn't remember the plot of the Doctor Who movie because there was none; Just a bunch of plot holes strung together.
  • rumplestilzkenrumplestilzken Posts: 181Member
    Gotchya. Thanks!

    I considered looking into Unity, but it feels like i would be cheating myself out of the development portion of the game you know? I know you can use the C++ plugin but that requires the pro version of Unity, plus i would feel a bit better doing the base level framework/engine myself and learning the different aspects of cross platform development and the low level details of each subsystem. It will take me awhile to get there, but once I'm there i think i will have a better understanding of how the overall system is working, ya know?

  • ShushShush Posts: 178Member
    Absoloutely agreed Rumple, wait until you get to the sound portion of your game, I spent a LOT of time researching Android sound and in the end whipped up an extremely efficient and reliable sound engine in OpenSL, that was a lot of fun.
  • TRUEtravTRUEtrav Posts: 57Member
    edited July 2013
    My C++-based engine already has input functionality for touch screens. It basically hands of input as delegate methods to the engine's current game state, and all the data per input device currently being tracked is also encapsulated into objects being managed by the current platform's device manager. Seem decent enough. I also noticed that the motion event method is SUPER sensitive as well, and so I attempted to block any joystick or trigger movement if it was deemed "too subtle" before making the expensive JNI call to send it to my native library for actual use. Same for OnKeyDown(): if the button was already pressed for that controller, don't process the event further.

    Is there a way to slow down event detection? Kind of like in iOS where the accelerometer and touch moved resolution can be lowered down to like 30 updates per second and actually give a speed boost in some cases. Here's how I attempt to weed out unnecessary motion events:

    @Override

    public boolean onGenericMotionEvent(MotionEvent event)

    {

    int playerIndex = OuyaController.getPlayerNumByDeviceId(event.getDeviceId());


    // handle joystick events

    if(event.getSource() == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE)

    {

    // only process analog events every 1/60 (0.016) seconds

    float timeDiff = ((float)event.getEventTime() / 1000.0f) - lastAnalogTime[playerIndex];

    if(timeDiff < 0.016f)

    return super.onGenericMotionEvent(event);

    // get the coordinates for both axes and the trigger values

    float xLeft = event.getAxisValue(MotionEvent.AXIS_X);

    float yLeft = -event.getAxisValue(MotionEvent.AXIS_Y);

    float xRight = event.getAxisValue(MotionEvent.AXIS_Z);

    float yRight = -event.getAxisValue(MotionEvent.AXIS_RZ);

    float lTrigger = event.getAxisValue(MotionEvent.AXIS_LTRIGGER);

    float rTrigger = event.getAxisValue(MotionEvent.AXIS_RTRIGGER);

    float xLeftDiff = Math.abs(xLeft - lastLeftX);

    float yLeftDiff = Math.abs(yLeft - lastLeftY);

    float xRightDiff = Math.abs(xRight - lastRightX);

    float yRightDiff = Math.abs(yRight - lastRightY);

    float lTriggerDiff = Math.abs(lTrigger - lastTriggerL);

    float rTriggerDiff = Math.abs(rTrigger - lastTriggerR);

    // update last frame data

    lastAnalogTime[playerIndex] = event.getEventTime() / 1000.0f;

    lastLeftX = xLeft;

    lastLeftY = yLeft;

    lastRightX = xRight;

    lastRightY = yRight;

    lastTriggerL = lTrigger;

    lastTriggerR = rTrigger;

    // make sure that the change in analog or trigger state is worth the JNI call

    if( xLeftDiff > kEpsilon || yLeftDiff > kEpsilon ||

    xRightDiff > kEpsilon || yRightDiff > kEpsilon ||

    lTriggerDiff > kEpsilon || rTriggerDiff > kEpsilon)

    NativeLib.OnAnalogMotion(playerIndex, xLeft, yLeft, xRight, yRight, lTrigger, rTrigger);

    }

    // handle mouse events

    if(event.getSource() == InputDevice.SOURCE_MOUSE)

    {

    //float xMouse = event.getX();

    //float yMouse = event.getY();

    }

    return super.onGenericMotionEvent(event);

    }


    This might be overkill and actually be an expensive culling optimization.

    Post edited by TRUEtrav on
  • ShushShush Posts: 178Member
    I tried something similar to this, then I decided to research just how expensive Java JNI to Native calls really are and to be expected there is no clear cut and dry answer. JNI calls to native can be almost as fast as Java calls, (this makes sense as in essence you have 1 layer of a shared library interface, which is not exactly heavy weight until you start passing in Java objects and data structures).

    Here's a decent link to this very question/presumption http://stackoverflow.com/questions/7699020/what-makes-jni-calls-slow

    So in the end I chose to pass all events through to my JNI code through intrinsic type parameters, (NO data structures or complex Java types), and keep the Java code as short and simple as possible.

    The big reasoning for me was that I did NOT want to induce the GC in any way, shape or form. I am not a 24-7 Java programmer, but what I know about the GC and the subtle ways that it can be invoked, (more like awakening ye ole apocalyptic dragon of doom), are nothing short of insanity to my eyes.

    So far I can detect no Java Event -> JNI -> C++ slowdowns and I never see the GC fire...Nirvana.
  • rumplestilzkenrumplestilzken Posts: 181Member
    I only call from Java>>C++ 1 time so far, and that is to initiate a refresh of gamepad cache data in C++. This is only necessary because i use the InputManager to determine that a gampad has been added or removed using an InputDeviceListener, and the the whole process that starts at start up is called again to make sure everything is in sync. 

    All my JNI calls from C++->Java i handle the references manually using the env->Delete* function. So i haven't had to deal with the GC much as far as i know.

    As far as speed goes, i haven't had any issues so far dealing with my input library. 

    Shush-- I look forward to getting into the other subsections once i have a rock solid input library to depend on. 
  • rumplestilzkenrumplestilzken Posts: 181Member
    PS, all input events have been handled in purely native code so far using the native_app_glue callback for input events, which i handle with a section of the input library I'm building.

    The way I'm thinking of doing it right now is buffering the input and having another thread react to that input. Because the way the polling works is if you keep providing input, it will just keep polling and never exit the standard ALooper_pollAll call and thus will not continue animating. 
  • ShushShush Posts: 178Member
    It would be extremely interesting to compare the two methods of NDK integration, (Java JNI Native, compared to Native JNI Java), on my particular projects.

    I'm guessing your approach has more setup time as you are required to cache Java objects and deal with threads, whereas I just simply wrap a minimal Java activity. But your approach obviously has much more potential for performance as once the initial setup has been processed you have much more control over when the JNI boundary is crossed.

    I'll be following your posts closely :)
  • rumplestilzkenrumplestilzken Posts: 181Member
    edited July 2013
    It took me about two days (after work) work to get things set up for the gamepad to be recognized in both java and c++ and such, i am still working on the threading and input processor, but once it is tested and complete i will fill you in on how everything worked!! (this is all taking into account i had never done native development for android, and had a HUGE learning curve as i had hardly done any android development at all! haha) 

    Plus in the end, i have a working implementation for Java code and Native. My basic idea is i want to make my library as maintainable as possible, and while i duplicate (i look at it more as one cohesive part of the input library, as Native relies on the Java code to work.. cant wait till the NDK is updated to work properly with gamepads.) some functionality on the java side.

    What i want is to have a native library that can be used reliably when porting from android to other systems as my major groundwork of this framework/engine is going to be done on the Ouya and (when it arrives, i preordered in May) my Nvidia Shield, then it will all be worked out on other platforms. So long as i consider that other platforms might work differently and abstract the code correctly i should be in a pretty good boat.
    Post edited by rumplestilzken on
  • ShushShush Posts: 178Member
    That sounds like a logical and efficient mechanism for coping with multiple platforms.

    I didn't even know the Nvidia Shield was a available for pre-order, thanks for the heads up.
  • rumplestilzkenrumplestilzken Posts: 181Member
    edited August 2013
    Agreed. lol. 

    No problem, the Shield went preorder in May, and actually you can pick it up in store at Gamestop and Microcenter if either are close to you (as of either yesterday or today)
    Post edited by rumplestilzken on
Sign In or Register to comment.