First go at the Android accelerometer

edited June 2015 in General discussions
I'm having a go at working with the accelerometer in Android devices.

I wanted to point out some unusual things about the naming of config elements and keywords.

To use the accelerometer, you add this to the config:
[Android]
AccelerometerFrequency     = 60
UseJoystick                = false

Which sets the frequency to 60 and ensures that connected joysticks or gamepads do not disable the accelerometer.

Next bit of config is adding the axis controllers:
[MainInput]
JOY_X_1     = GravityX
JOY_Y_1     = GravityY
JOY_Z_1     = GravityZ


Add an input handler in to catch the accelerometer events:
orxEvent_AddHandler(orxEVENT_TYPE_INPUT, InputEventHandler);

And then in the handler:
if (_pstEvent->eType == orxEVENT_TYPE_INPUT) {
    if (_pstEvent->eID == orxINPUT_TYPE_MOUSE_AXIS){
        orxLOG("X: %f
 Y: %f
 Z: %f", orxInput_GetValue("GravityX"), orxInput_GetValue("GravityY"), orxInput_GetValue("GravityZ"));
    }
}

And that all works pretty sweet. Where it had me confused is the names of all the elements.

Working backwards, orxINPUT_TYPE_MOUSE_AXIS refers to a mouse axis.

MainInput in the config defines the axis as a joystick, eg: JOY_X_1. It's not clear how the Android accelerometer has any relationship with joysticks.

And finally, the Android config section has a UseJoystick entry. While it's clear in the docs what this is, the joystick keyword part seems confusing based on the other elements.

Am I right that this is all a little tricky to figure out?

Comments

  • edited June 2015
    sausage wrote:
    I'm having a go at working with the accelerometer in Android devices.

    I wanted to point out some unusual things about the naming of config elements and keywords.

    To use the accelerometer, you add this to the config:
    [Android]
    AccelerometerFrequency     = 60
    UseJoystick                = false
    

    Which sets the frequency to 60 and ensures that connected joysticks or gamepads do not disable the accelerometer.

    Also note that UseJoystick is set to false by default, so you shouldn't have to worry about it generally speaking.
    Next bit of config is adding the axis controllers:
    [MainInput]
    JOY_X_1     = GravityX
    JOY_Y_1     = GravityY
    JOY_Z_1     = GravityZ
    


    Add an input handler in to catch the accelerometer events:
    orxEvent_AddHandler(orxEVENT_TYPE_INPUT, InputEventHandler);
    

    And then in the handler:
    if (_pstEvent->eType == orxEVENT_TYPE_INPUT) {
        if (_pstEvent->eID == orxINPUT_TYPE_MOUSE_AXIS){
            orxLOG("X: %f
     Y: %f
     Z: %f", orxInput_GetValue("GravityX"), orxInput_GetValue("GravityY"), orxInput_GetValue("GravityZ"));
        }
    }
    

    There's a bit of confusion here. :)
    You're testing the event->eID with an enum that isn't related to it (orxINPUT_TYPE). The event you want is orxINPUT_EVENT. You can check if the event is about an input on or off, for example. The payload will contain all the specifics. Also you don't need to use the combination of event + poll API. Usually it's an either/or situation, either you listen to the events or you call the orxInput_* API.

    In this situation, you could simply move your orxLOG() line inside any Update() call and it would work all the same. :)
    And that all works pretty sweet. Where it had me confused is the names of all the elements.

    I'd call it a bit of luck on this one. ;)
    Working backwards, orxINPUT_TYPE_MOUSE_AXIS refers to a mouse axis.

    It does, but as we've just since it's totally unrelated to the situation. :)
    MainInput in the config defines the axis as a joystick, eg: JOY_X_1. It's not clear how the Android accelerometer has any relationship with joysticks.

    There are two direct equivalences that you need to be aware of:
    - mouse <-> touch #1
    - joystick #1 <-> accelerometer

    That allows one to easily test single touch and/or accelerometer on a computer by using the mouse and a joystick.

    Unfortunately, now Android devices also got joystick support, hence the UseJoystick variable that lydesik had to add to know which device one want to listen to.
    And finally, the Android config section has a UseJoystick entry. While it's clear in the docs what this is, the joystick keyword part seems confusing based on the other elements.

    It is an actual "joytick", and not an accelerometer. It's aimed at the new wave of Android-based console/TVs were one can plug a gamepad as input.
    Am I right that this is all a little tricky to figure out?

    The only tricky part is the two equivalences, I think, the rest was based on mistakes.

    To sum it up:
    - there are two computer<->mobile equivalences made: mouse <-> touch #1 + joystick #1 <-> accelerometer
    - there's no relations between mouse and accelerometer
    - one should either use input events or the polling API, but not both at once
    - there are lower level events to get directly notified of touches and accelerometer changes (orxEVENT_TYPE_SYSTEM), bypassing the input module. Those can be useful sometimes and can't be avoided in the case of a multi-touch applications as only the touch #1 can be retrieved via the input module at the moment.

    Lemme know if there are any other blurry areas. :)
  • edited June 2015
    just wanted to add:

    to use the accelerometer as an orx joystick input, you don't need to add anything to the config. it's on by default with a default frequency (don't remember the value)

    (btw: the frequency here is a hint for the device, nothing guarantee that's the actual update frequency you'll get)

    if you want to disable the accelerometer use
    AccelerometerFrequency     = 0
    

    this will completely disable the accelerometer and reduce battery usage. and of course AccelerometerFrequency is not used at all if UseJoystick is set to true.
  • edited June 2015
    iarwain wrote:
    There's a bit of confusion here. :)
    You're testing the event->eID with an enum that isn't related to it (orxINPUT_TYPE). The event you want is orxINPUT_EVENT. You can check if the event is about an input on or off, for example. The payload will contain all the specifics. Also you don't need to use the combination of event + poll API. Usually it's an either/or situation, either you listen to the events or you call the orxInput_* API.

    Ah yes, I am totally mixing input concepts. This isn't the first time I've done this. Ahrrggh.

    I did wonder if I was using the wrong enums, but I just couldn't see it last night. This morning, it seems so obvious especially now that you pointed it out.

    iarwain wrote:
    There are two direct equivalences that you need to be aware of:
    - mouse <-> touch #1
    - joystick #1 <-> accelerometer

    Ah that's a very important piece of info. I am not sure this is explained anywhere else. There's no real details of using accelerometers in the wiki, forum or mailing list so I'll pop something together.

    Thanks very much for all this. I'll recode this up properly and repost.
  • edited June 2015
    Thanks, lydesik. Most of my info came from the demo and the SettingsTemplate.ini. Is there any other reference material available?
  • edited June 2015
    sausage wrote:
    iarwain wrote:
    There are two direct equivalences that you need to be aware of:
    - mouse <-> touch #1
    - joystick #1 <-> accelerometer

    Ah that's a very important piece of info. I am not sure this is explained anywhere else. There's no real details of using accelerometers in the wiki, forum or mailing list so I'll pop something together.

    Ah yes, sorry about that. It's hard for me to keep track of what is properly documented and what isn't, that's why questions and wiki work by you and the community is invaluable to get the relevant pieces of information exposed.
    I know that it has been mentioned a few times on the forum, but it's definitely not the easiest place where to search for info, unfortunately.

    As for the SettingsTemplate.ini, there should be a wiki page giving more details on this whole file, but it might not have been updated on that specific point.
  • edited June 2015
    iarwain wrote:
    sausage wrote:
    Ah yes, sorry about that. It's hard for me to keep track of what is properly documented and what isn't, that's why questions and wiki work by you and the community is invaluable to get the relevant pieces of information exposed.
    I know that it has been mentioned a few times on the forum, but it's definitely not the easiest place where to search for info, unfortunately.

    No that's all good, this is the way it should work. We complain - you explain - we write. :)

    That way you don't need to get bogged down keeping track of every area.


    iarwain wrote:
    sausage wrote:
    As for the SettingsTemplate.ini, there should be a wiki page giving more details on this whole file, but it might not have been updated on that specific point.


    This is a tricky one because it's good to not have to keep track of the same thing in both places. The various sections are broken out of that file and placed in the wiki with more detail. But it does get out of date.

    Perhaps us lot just need to keep it up to date in the wiki with the bitbucket version (although the unstable doesn't match the latest release).

    Yeah it's messy....
  • edited June 2015
    Actually some extra thoughts...

    Something like SettingsTemplate.ini, we can continue to break up the pieces for the wiki sections, but mark the new features as bleeding edge only.

    That way the wiki pages can be read for both bleeding edge or stable entries.

    So something like this:
    [Screenshot]
    Directory = path/to/directory; NB: If not specified, screenshots will be stored in the Documents directory on iOS or in the current active directory otherwise;
    SomeNewMadeUpEntity = Value
    
    [SomeNewMadeUpSection]
    MadeUpEntity = Value
    

    Could become:
    [Screenshot]
    Directory = path/to/directory; NB: If not specified, screenshots will be stored in the Documents directory on iOS or in the current active directory otherwise;
    SomeNewMadeUpEntity = Value <-- Only available for bleeding edge
    
    [SomeNewMadeUpSection] <-- Only available for bleeding edge
    MadeUpEntity = Value
    
  • edited June 2015
    Played around again, Have I got the enumerations right?. Does this seem right?:
    orxSTATUS orxFASTCALL InputEventHandler(const orxEVENT *_pstEvent)
    {
        orxINPUT_EVENT_PAYLOAD *pstPayload;
        pstPayload = (orxINPUT_EVENT_PAYLOAD *)_pstEvent->pstPayload;
    
        if (_pstEvent->eType == orxEVENT_TYPE_INPUT) {
            if (_pstEvent->eID == orxINPUT_EVENT_ON) {
                //never fires
            }
            if (_pstEvent->eID == orxINPUT_EVENT_OFF) {
                //never fires
            }
            if (_pstEvent->eID == orxINPUT_EVENT_OFF) {
                //never fires
            }
            if (_pstEvent->eID ==  orxINPUT_EVENT_SELECT_SET) {
                if (orxString_Compare(pstPayload->zSetName, "MainInput") == 0) {
                    float x = orxInput_GetValue("GravityX");
                    float y = orxInput_GetValue("GravityY");
                    float z = orxInput_GetValue("GravityZ");
                    orxLOG("X: %f Y: %f Z: %f", x, y, z);
                }
            }
            if (_pstEvent->eID ==   orxINPUT_EVENT_NUMBER) {
                //never fires
            }
    

    Additionally: pstPayload->afValue always contains the GravityZ value (maybe because its last in the MainInput list) and pstPayload->zInputName is always null.

    The orxINPUT_EVENT_SELECT_SET fires twice each time with the same values (which seems to indicate an ON and OFF event pairs).

    I'm getting it wrong eh?
  • edited June 2015
    Well, definitely not wrong, but maybe not the right expectations. ;)

    You should receive the ON event once. At the very beginning. And never the OFF one. Unless you find a way to cancel gravity. ;)

    In the case of accelerometer, you should simply do polling where you need (orxInput_GetValue()) and not wait for events as the accelerometer will probably never return a 0 value. =)

    As for the SELECT_SET received twice a frame, it's probably when the console activates its input set and restore the game's one. The only relevant info in the payload in this case is the name of the set that has been selected. I'm actually surprised you get any non-zero value in the rest of the payload, including the whole afValue array which should be filled with 0s.
  • edited June 2015
    iarwain wrote:
    You should receive the ON event once. At the very beginning. And never the OFF one. Unless you find a way to cancel gravity. ;)

    Can we consider this a bug on the Android side? orxINPUT_EVENT_SELECT_SET is the only event ever fired, so matter how hard I shake it :)

    iarwain wrote:
    In the case of accelerometer, you should simply do polling where you need (orxInput_GetValue()) and not wait for events as the accelerometer will probably never return a 0 value. =)

    Yes I will switch to doing it this way. I guess I would have liked to keep it organised in the event system with the other inputs like the keyboard, but I can certainly work around this without too much trouble.


    iarwain wrote:
    As for the SELECT_SET received twice a frame, it's probably when the console activates its input set and restore the game's one. The only relevant info in the payload in this case is the name of the set that has been selected. I'm actually surprised you get any non-zero value in the rest of the payload, including the whole afValue array which should be filled with 0s.

    Those other values certainly aren't 0. In fact afValue == aeID == aeType which doesn't seem right.

    I have the:

    if (orxString_Compare(pstPayload->zSetName, "MainInput")

    to filter out the console inputs, but perhaps, has you say it doesn't filter out the activating and restoring of input sets.
  • edited June 2015
    sausage wrote:
    iarwain wrote:
    You should receive the ON event once. At the very beginning. And never the OFF one. Unless you find a way to cancel gravity. ;)

    Can we consider this a bug on the Android side? orxINPUT_EVENT_SELECT_SET is the only event ever fired, so matter how hard I shake it :)

    Well that was my point actually, it's not a bug. The ON event is fired when an input goes from inactive to active, and the OFF event when an input goes from active to inactive. In the case of an accelerometer, the input is always active. Even if you do not move your phone, gravity is applied to it constantly and generates an acceleration. So unless you are in space outside any gravitational field, that input will not have on/off cycles.
    Yes I will switch to doing it this way. I guess I would have liked to keep it organised in the event system with the other inputs like the keyboard, but I can certainly work around this without too much trouble.

    My recommendation would actually to always use the poll interface unless you really have to go with the events. And the only case where you can't avoid events are multi-touch interactions. Eventually, at some point in the future, those will get included in the polling system as well too.
    Those other values certainly aren't 0. In fact afValue == aeID == aeType which doesn't seem right.

    I'm a bit confused by what you mean by the value of afValue. It's an array, not a variable. afValue can't have a value of its own, afValue[0] can, and it should be 0.0f. Same applies to aeID and aeType, they're all arrays containing 4 elements.
    I have the:

    if (orxString_Compare(pstPayload->zSetName, "MainInput")

    to filter out the console inputs, but perhaps, has you say it doesn't filter out the activating and restoring of input sets.

    You'll then filter out the activation, but not the restoration as the sequence is:
    orxInput_SelectSet("-=ConsoleSet=-");
    // Console input tests
    orxInput_SelectSet("MainInput");
    
  • edited June 2015
    iarwain wrote:
    Well that was my point actually, it's not a bug.

    Ah right yep. Misread that.

    iarwain wrote:
    My recommendation would actually to always use the poll interface unless you really have to go with the events.

    No I don't really have to use events. It's not a problem. I've tried polling tonight within in the Run() function and it's working pretty well.

    iarwain wrote:
    I'm a bit confused by what you mean by the value of afValue. It's an array, not a variable. afValue can't have a value of its own, afValue[0] can, and it should be 0.0f. Same applies to aeID and aeType, they're all arrays containing 4 elements.

    I'll double check this. I had all values writing to the log and each value was the same. But I could have made a mistake. I'll see if I can reproduce it.
  • edited June 2015
    In my waking hours, I finally get it.

    What is the point to filtering for an event, checking it's type, unpacking the value, working out which is which, then using it, when the values are simply available all the time and are streamed constantly?

    Sometimes you just have let the dog out to chase it's own tail until it simply runs out of puff.

    ;)
Sign In or Register to comment.