[Solved] Detecting generic Gamepad D-Pad on Android

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

[Solved] Detecting generic Gamepad D-Pad on Android

Post by Ats »

Hello. Today, I'm debugging the Gamepad D-Pad on Android. I remember it worked on OUYA years ago, maybe because they were buttons.
Turns out that on most generic gamepads, they are not buttons, nor an axis, but a hat... (I used the app Gamepad Tester to list all the gamepad buttons).
So I'm wondering how to detect it. For starters, is a hat a POV?

I tried joyGetPOV, and after some search I found that bit of code in ChuckieEgg.zgeproj.
(Maybe we should add some explanations in the ZGE's help regarding the use of joyGetPOV?)

Code: Select all

float pov=joyGetPOV(0);
if(pov != -1 ) {
  const int range=45;
  if( (pov>= 90-range) && (pov<=90+range) )
    buttons |= BUTTON_RIGHT;
  else if( (pov>=270-range) && (pov<=270+range) )
    buttons |= BUTTON_LEFT;
  if( (pov>= 0-range) && (pov<=0+range) )
    buttons |= BUTTON_UP;
  else if( (pov>=180-range) && (pov<=180+range) )
    buttons |= BUTTON_DOWN;
}
But I can't get anything from that, as it always return -1.
What could it be, then?
Last edited by Ats on Mon Jan 09, 2023 9:01 am, edited 7 times in total.
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Detecting generic Gamepad D-Pad on Android

Post by Ats »

With a lot of different USB dongles in chain in order to plug the gamepads for testing on Android, and some sideloading too, here are the results of my research on D-Pad:

Axis 0x0f HAT_X / Axis 0x10 HAT_Y
  • Retroid Pocket 3+
  • XBox Controller
  • NVidia Shield TV controller
Axis 0x00 AXIS X / Axis 0x01 AXIS Y
  • Thrustmaster Firestorm Digital 2
  • 8BitDo NES Gamepad (old edition)
Key 0 UP 19 / Key 0 DOWN 20 / Key 0 LEFT 21 / Key 0 RIGHT 22
  • TV remote control
Not working because it needs even weirder drivers on Android and PC to work...
  • PS3 Dualshock wireless controller

(I'm still searching for AA batteries in order to test the OUYA gamepad)


Edit:
All right, I found a box full of leaking rechargeable batteries. Making a game is a hard journey.

Key 312 BUTTON_L2 104 (up) / Key 313 BUTTON_R2 105 (down) / Key 314 BUTTON_SELECT 109 (left) / Key 315 BUTTON_SELECT 108 (right)
  • OUYA Gamepad
The OUYA was definitely a weird hardware :lol:
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Detecting generic Gamepad D-Pad on Android

Post by Kjell »

Hi Ats,
Ats wrote: Fri Jan 06, 2023 10:52 pmFor starters, is a hat a POV?
POV stands for point-of-view control or point-of-view hat. I suspect Ville adopted this term from the WinMM API.
Ats wrote: Fri Jan 06, 2023 10:52 pmMaybe we should add some explanations in the ZGE's help regarding the use of joyGetPOV?
There's not much to it .. it returns the angle of the POV hat in degrees ( up = 0, left = 270 ) or when the hat is centered the value is -1.

Anyway, the problem is that joyGetPOV isn't implemented in ZGE for Android, it simply always returns -1.
EDIT - Scratch that .. apparently there's no POV concept on Android, so that's why it always returns -1.

K
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Detecting generic Gamepad D-Pad on Android

Post by Ats »

All right. But why is it returning an angle when all gamepads' hat works with two axis, just like a joystick? Doesn't the Nintendo Switch Ville talked about in his ChuckieEgg example work like that?

I'll see how the joystick work in the source and if I can add the hat for Android when I'll get home :wink:
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Detecting generic Gamepad D-Pad on Android

Post by Kjell »

Hi Ats,
Ats wrote: Sat Jan 07, 2023 1:25 pmBut why is it returning an angle when all gamepads' hat works with two axis, just like a joystick?
It's up to the manufacturer ( and driver programmer specifically ) to decide how they want to represent their D-Pad in any particular API. As you noticed in your research, there's no consensus. On Windows some manufacturers go with 4 individual buttons ( TRUE of FALSE ), some go with 2 analog axes ( floating-point values between -1 and 1 ) and some go with POV ( integer value between 0 and 359 ).
Ats wrote: Sat Jan 07, 2023 1:25 pmDoesn't the Nintendo Switch Ville talked about in his ChuckieEgg example work like that?
I suspect Ville was using a Joy-Con on PC, which requires custom / unofficial drivers .. and again, it's entirely up to the programmer of that driver how they want to represent the D-Pad in any particular API.

By the way, AXIS_HAT_X ( 0x0F ) and AXIS_HAT_Y ( 0x10 ) are simply axis index constants used in conjuction with getAxisValue.

K
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Detecting generic Gamepad D-Pad on Android

Post by Ats »

Thanks, that was a super helpful hint. So if hat is just a joystick axis, then I should be able to simply retrieve:

Code: Select all

joyX = joyGetAxis(0,15); // 0x0F
joyY = joyGetAxis(0,16); // 0x10
Unfortunately, joyGetAxis() prevents to get an axis bigger than MAX_AXES = 6;

Code: Select all

function Platform_GetJoystickAxis(JoyId : integer; Axis : integer) : single;
begin
  Result := 0;
  if JoyId<MAX_JOYSTICKS then
  begin
    [b]if Axis<MAX_AXES then[/b]
      Result := Joysticks[JoyId].Values[Axis];
  end;
end;
I could augment MAX_AXES or simply remove the test, but I wonder why there is this test in the first place. Since we pass the JoyId and Axis manually to the function, why limiting this number? There is no cycle. Maybe it crashes?

Edit:
Nope... I commented out "if Axis<MAX_AXES then", compiled libzgeandroid.so and run a wild:

Code: Select all

JoystickLeftX = JoystickLeftY = 0;
for (int i=0; i<4; i++) {
  for (int j=0; j<20; j+=2) {
    if (joyGetAxis(i,j) != 0) JoystickLeftX = joyGetAxis(i,j);
    if (joyGetAxis(i,j+1) != 0) JoystickLeftY = -joyGetAxis(i,j+1);
  }
}
But only the left and right joysticks are caught on the gamepads. Not the D-Pad :|
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Detecting generic Gamepad D-Pad on Android

Post by Ats »

Maybe I need to add the MotionEvent.AXIS_HAT_X and MotionEvent.AXIS_HAT_Y values in Zge.java?

Code: Select all

private native void NativeSetJoyAxisValue(int joyId, int axisNr, float value);
(...)
// joystick
if ( (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK ) != 0) {

  int controllerNb = getControllerNumber(event.getDeviceId());

  NativeSetJoyAxisValue(controllerNb,
      0, // MotionEvent.AXIS_X or OuyaController.AXIS_LS_X
      event.getAxisValue(MotionEvent.AXIS_X));

  NativeSetJoyAxisValue(controllerNb,
      1, // MotionEvent.AXIS_Y or OuyaController.AXIS_LS_Y
      event.getAxisValue(MotionEvent.AXIS_Y));

  NativeSetJoyAxisValue(controllerNb,
      2, // MotionEvent.AXIS_Z or OuyaController.AXIS_RS_X
      event.getAxisValue(MotionEvent.AXIS_Z));

  NativeSetJoyAxisValue(controllerNb,
      3, // MotionEvent.AXIS_RZ or OuyaController.AXIS_RS_Y
      event.getAxisValue(MotionEvent.AXIS_RZ));

  NativeSetJoyAxisValue(controllerNb,
      4, // MotionEvent.AXIS_LTRIGGER or OuyaController.AXIS_L2
      event.getAxisValue(MotionEvent.AXIS_LTRIGGER));

  NativeSetJoyAxisValue(controllerNb,
      5, // MotionEvent.AXIS_RTRIGGER or OuyaController.AXIS_R2
      event.getAxisValue(MotionEvent.AXIS_RTRIGGER));

  NativeSetJoyAxisValue(controllerNb,
      6, // MotionEvent.AXIS_LTRIGGER or OuyaController.AXIS_L2
      event.getAxisValue(MotionEvent.AXIS_RX));

  NativeSetJoyAxisValue(controllerNb,
      7, // MotionEvent.AXIS_RTRIGGER or OuyaController.AXIS_R2
      event.getAxisValue(MotionEvent.AXIS_RY));

  return true;
}
But I'm not really sure of what is going on here, since the axisNr numbers doesn't correspond to the axis numbers from the Android Developers.

Edit:
Before going to bed, I tried adding

Code: Select all

  NativeSetJoyAxisValue(controllerNb,
      8, // MotionEvent.AXIS_HAT_X
      event.getAxisValue(MotionEvent.AXIS_HAT_X));

  NativeSetJoyAxisValue(controllerNb,
      9, // MotionEvent.AXIS_HAT_Y
      event.getAxisValue(MotionEvent.AXIS_HAT_Y));
without success :lol:
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Detecting generic Gamepad D-Pad on Android

Post by Kjell »

Hi Ats,
Ats wrote: Sat Jan 07, 2023 8:20 pmI could augment MAX_AXES or simply remove the test, but I wonder why there is this test in the first place. Since we pass the JoyId and Axis manually to the function, why limiting this number?
Because ZGE allocates a block of memory to hold the values for MAX_AXES amount of axes. If you'd remove the condition(s) ZGE could end up reading ( or writing ) memory that is used for something else entirely, or worse unallocated memory ( which would cause a crash ).
Ats wrote: Sat Jan 07, 2023 9:28 pmMaybe I need to add the MotionEvent.AXIS_HAT_X and MotionEvent.AXIS_HAT_Y values in Zge.java?
That appears to be the easiest way to do it yes.
Ats wrote: Sat Jan 07, 2023 9:28 pmBut I'm not really sure of what is going on here, since the axisNr numbers doesn't correspond to the axis numbers
The axisNr numbers correspond to the axisNr argument in the joyGetAxis(joyId,axisNr) function called from scripts in ZGE.

Curiously, MAX_AXES is set to 6 on Android while Ville actually mapped 8 axes .. so the last 2 ( AXIS_RX / AXIS_RY ) will always fail the MAX_AXES condition and never get written to memory. Probably just a little mistake / oversight.

K
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Detecting generic Gamepad D-Pad on Android

Post by Ats »

Everything was broken, so I reverted all, but it turns out it was because I removed one android lib by mistake...

So, with MAX_AXES = 10; and the addition of

Code: Select all

  NativeSetJoyAxisValue(controllerNb,
      8, // MotionEvent.AXIS_HAT_X
      event.getAxisValue(MotionEvent.AXIS_HAT_X));

  NativeSetJoyAxisValue(controllerNb,
      9, // MotionEvent.AXIS_HAT_Y
      event.getAxisValue(MotionEvent.AXIS_HAT_Y));
in Zge.java, the D-Pad is now working with joyGetAxis(0,8) and joyGetAxis(0,9).

Since those axis numbers are just the order numbers in the Zge.java list, there really should be some explanations in the help file :lol:

Anyway, those Android D-pads are now working. I'm going to adapt my game to that while waiting for Ville to see if my fix is ok, or if it could mess with the memory.

Thanks again Kjell!
User avatar
VilleK
Site Admin
Posts: 2274
Joined: Mon Jan 15, 2007 4:50 pm
Location: Stockholm, Sweden
Contact:

Re: Detecting generic Gamepad D-Pad on Android

Post by VilleK »

Ats wrote: Sun Jan 08, 2023 6:29 pm Anyway, those Android D-pads are now working. I'm going to adapt my game to that while waiting for Ville to see if my fix is ok, or if it could mess with the memory.
Looks fine to me! As long as you update the MAX_AXES constant then I don't see there should be any risk of overwriting memory.
Post Reply