Share My Creation Generic GamePad Support (Source Code)

GENERIC GAMEPAD SUPPORT
Latest update: 19-05-2015

Many thanks to thedesolatesoul for his help with the getDeviceName function.


Fellow gamers and game developers, please test this app with your game controllers.
It works flawlessly with my PS3 one (using the Sixaxis Controller app), but it should work with any other brand recognized by your android device.

If like me, you're planning to use a PlayStation controller, please follow this tutorial:
Using a PlayStation 3 controller with your Android Device

Note: The Sixaxis app with root access is required only for PS3 / PS4 controllers.

Download the apk or the full project below (please use the updated GamePad.bas class file), the additional library is included.

Capture.PNG


APK:
Usage:
As you can see in the source code, the analog sticks and triggers are handled by Activity_OnGenericMotionEvent and the regular buttons by Activity_KeyPress. I've tried the best I could to make the code easier to understand, so everything should be self-explanatory.

Libraries:

Class (2016-28-12):
GamePad.bas (attached)

Documentation:

Source:
B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Generic GamePad Support
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: True
    #IncludeTitle: False
    #Extends: anywheresoftware.b4a.objects.ActivityEx
#End Region

Sub Process_Globals
    'Define the Main Cycle timer
        Dim Main_Cycle As Timer
End Sub

Sub Globals
    'REQUIRED
        Dim Controller As GamePad

    'Used solely for this example
        Dim Mode = "NORMAL" As String

    'Designer Variables
        Private Background As ImageView
        Private Output As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Load the Activity Layout
        Activity.LoadLayout("Layout1")

    'Setup the Text and Background
        Background.Width = 100%x
        Background.Height = (100%x) * 720 / 1280
        Background.Left = 50%x - Background.Width / 2
        Background.Top = 50%y - Background.Height / 2

        Output.Left   = Background.Left + (0.054 * Background.Width)
        Output.Top    = Background.Top  + (0.116 * Background.Height)
        Output.Width  = 0.289 * Background.Width
        Output.Height = 0.625 * Background.Height

    'Initialize the first GamePad with a dead zone of 10%
        Controller.Initialize(0, 0.1)

    'Initialize the Main Cycle timer
        Main_Cycle.Initialize("Main_Cycle", 1000/60)
        Main_Cycle.Enabled = True
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub


'========================================================================================================================
' MAIN CYCLE
'========================================================================================================================
    Sub Main_Cycle_Tick
        If Controller.Plugged_In Then
            If Mode = "NORMAL" Then
                Output.Text = _
                Controller.Device_Name & CRLF & _
                CRLF & _
                "Analog LX: " & Round(Controller.L_Analog_X * 100) & "%" & CRLF & _
                "Analog LY: " & Round(Controller.L_Analog_Y * 100) & "%" & CRLF & _
                "Analog RX: " & Round(Controller.R_Analog_X * 100) & "%" & CRLF & _
                "Analog RY: " & Round(Controller.R_Analog_Y * 100) & "%" & CRLF & _
                "L Trigger: " & Round(Controller.L_Trigger  * 100) & "%" & CRLF & _
                "R Trigger: " & Round(Controller.R_Trigger  * 100) & "%" & CRLF & _
                CRLF & _
                "Last KeyCode: " & Controller.Btn_Press & CRLF & _
                CRLF & _
                "Press START for Device Info" & CRLF & _
                "or SELECT To exit."
            Else If Mode = "DEVICE_INFO" Then
                If Controller.Device_Info = "" Then
                    Output.Text = "Please move one of the Analog Sticks / Triggers."
                Else
                    Output.Text = Controller.Device_Info & CRLF & _
                    CRLF & _
                    "Press START to begin" & CRLF & _
                    "or SELECT To exit."
                End If
            End If
        Else
            Output.Text = Controller.Device_Name & CRLF & _
            CRLF & _
            "Please plug you controller via USB" & CRLF & _
            "or pair it via Bluetooth to begin." & CRLF & _
            CRLF & _
            "Once you have it connected," & CRLF & _
            "press START to begin."
        End If
    End Sub
'========================================================================================================================




'========================================================================================================================
' BUTTON PRESS (KEYCODE) AND ANALOG AXIS / TRIGGER (GENERIC MOTION EVENT) HANDLING
'========================================================================================================================
    Sub Activity_KeyPress (KeyCode As Int) As Boolean
        Controller.Btn_Press = KeyCode
        If KeyCode = Controller.KEYCODE_BUTTON_START Then
            If Controller.Plugged_In Then
                If Mode = "NORMAL" Then
                    Mode = "DEVICE_INFO"
                Else
                    Mode = "NORMAL"
                End If
            Else
                Controller.Initialize(0, 0.1)
            End If
        Else If KeyCode = Controller.KEYCODE_BUTTON_SELECT OR KeyCode = KeyCodes.KEYCODE_BACK Then
            ExitApplication
        End If
        Return True
    End Sub


    Sub Activity_OnGenericMotionEvent(Event As Object)
        Controller.Input = Event

        Controller.Motion_Detected = True

        Controller.L_Analog_X  = Controller.getAxisValue(Controller.AXIS_X)
        Controller.L_Analog_Y  = Controller.getAxisValue(Controller.AXIS_Y)

        Controller.R_Analog_X  = Controller.getAxisValue(Controller.AXIS_Z)
        Controller.R_Analog_Y  = Controller.getAxisValue(Controller.AXIS_RZ)

        Controller.L_Trigger   = Controller.getAxisValue(Controller.AXIS_LTRIGGER)
        Controller.R_Trigger   = Controller.getAxisValue(Controller.AXIS_RTRIGGER)

        If Controller.Device_Info = "" Then Controller.Device_Info = Controller.getDeviceInfo
    End Sub
'========================================================================================================================
 

Attachments

  • ggs.png
    ggs.png
    32.5 KB · Views: 8,808
  • FULL PROJECT - GamePad.zip
    318.7 KB · Views: 1,306
  • GamePad.bas
    12.2 KB · Views: 888
Last edited:

wonder

Expert
Licensed User
Longtime User
HOW CAN I IMPLEMENT THIS FRAMEWORK ON MY PROJECT?

I know the ideal solution is to have a lib which handles everything, but since my java/lib experience is virtually zero, the way to implement my solution is a little bit fragmented.

Step 1:
Add both Generic Motion Event and JavaObject libraries to your project.
Step 2:
Add the GamePad class to your project.
Step 3:
Add the following code to your project:
B4X:
#Region  Activity Attributes
     #Extends: anywheresoftware.b4a.objects.ActivityEx
#End Region

Sub Globals
   Dim Controller As GamePad
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Controller.Initialize(0, 0.1) 'Initializes the first GamePad with a dead zone of 10%
End Sub
Step 4:
Handle the events with Activity_OnGenericMotionEvent for analog sticks and triggers and Activity_KeyPress for the controller buttons.
Example:
B4X:
Sub Activity_KeyPress (KeyCode As Int) As Boolean      
    Controller.Btn_Press = KeyCode
    If KeyCode = Controller.KEYCODE_BUTTON_START Then
        If Controller.Plugged_In Then
            If Mode = "NORMAL" Then
                Mode = "DEVICE_INFO"
            Else
                Mode = "NORMAL"
            End If
        Else
            Controller.Initialize(0, 0.1)
        End If
    Else If KeyCode = Controller.KEYCODE_BUTTON_SELECT OR KeyCode = KeyCodes.KEYCODE_BACK Then
        ExitApplication  
    End If      
    Return True
End Sub


Sub Activity_OnGenericMotionEvent(Event As Object)
    Controller.Input = Event
  
    Controller.Motion_Detected = True
  
    Controller.L_Analog_X  = Controller.getAxisValue(Controller.AXIS_X)
    Controller.L_Analog_Y  = Controller.getAxisValue(Controller.AXIS_Y)

    Controller.R_Analog_X  = Controller.getAxisValue(Controller.AXIS_Z)
    Controller.R_Analog_Y  = Controller.getAxisValue(Controller.AXIS_RZ)
  
    Controller.L_Trigger   = Controller.getAxisValue(Controller.AXIS_LTRIGGER)
    Controller.R_Trigger   = Controller.getAxisValue(Controller.AXIS_RTRIGGER)  

    If Controller.Device_Info = "" Then Controller.Device_Info = Controller.getDeviceInfo      
End Sub
Feel free to improve my code! If so, don't forget to share your own solution. :)
 
Last edited:

andymc

Well-Known Member
Licensed User
Longtime User
This is awesome, brilliant work! I've got a lot on right now, but will try to integrate this with my space invaders clone and work it into a few games that are currently in the early stages right now.
 

wonder

Expert
Licensed User
Longtime User
Hey guys,

Check the first and second post again.
I've cleaned-up and updated the source code, class and apk file.
The internal functions are now tucked in the GamePad.bas class, including thedesolatesoul's getDeviceName (thanks!).

Please update your projects accordingly.
 
Last edited:

freedom2000

Well-Known Member
Licensed User
Longtime User
HI,

It's quite a long time since you posted this code...
I promized to try it and I did it this evening :)

It WORKs --> simply GREAT

I have slightly changed the code to discover the "hat buttons".
Here is the mod I did :

B4X:
    Controller.L_Trigger   = Controller.getAxisValue(Controller.AXIS_HAT_X)
        Controller.R_Trigger   = Controller.getAxisValue(Controller.AXIS_HAT_Y)

I am not sure to have modified the right thing (What are L_Trigger and R_Trigger ?) but at least it works !

Again thank you

I will for sure quote your lib in a few days if I take some time to implement a fully working RC radio with android, B4A, your lib and an ESP8266
 

wonder

Expert
Licensed User
Longtime User
HI,

It's quite a long time since you posted this code...
I promized to try it and I did it this evening :)

It WORKs --> simply GREAT

I have slightly changed the code to discover the "hat buttons".
Here is the mod I did :

B4X:
    Controller.L_Trigger   = Controller.getAxisValue(Controller.AXIS_HAT_X)
        Controller.R_Trigger   = Controller.getAxisValue(Controller.AXIS_HAT_Y)

I am not sure to have modified the right thing (What are L_Trigger and R_Trigger ?) but at least it works !

Again thank you

I will for sure quote your lib in a few days if I take some time to implement a fully working RC radio with android, B4A, your lib and an ESP8266
Hello hello!

I'm really glad to hear that it works fine for you! :)
Every controller is slightly different so it's only normal we always have to tweak the button assignments here and there.
The original source code was modeled based on a Playstation 3 controller.

If I had the time, I'd like to turn this project into a simple (and single) library, but as I said, I'm really glad it works this way!
 

freedom2000

Well-Known Member
Licensed User
Longtime User
I will for sure quote your lib in a few days if I take some time to implement a fully working RC radio with android, B4A, your lib and an ESP8266
--> Done for my "Joystick to PPM" solution

--> Still to be done for the full RC controller with ESP8266...
 

sorex

Expert
Licensed User
Longtime User
are many people using these gamepads?

and are some games limited to use one?
 

wonder

Expert
Licensed User
Longtime User
are many people using these gamepads?
Probably not, it a niche part of the market. Nonetheless, we are at least 100,000 around the world, jugging by the download number of the Sixaxis Controller paid app (https://play.google.com/store/apps/details?id=com.dancingpixelstudios.sixaxiscontroller&hl=en).

and are some games limited to use one?
Can Android handle more than one controller at the same time?
I've seen apps that support up to 4 controllers at the same time, so it's up to the developer to implement such feature.
Docs: https://developer.android.com/training/game-controllers/multiple-controllers.html

and are some games limited to use one?
Are there any games which explicitly require a physical controller?
Yes. Most of NVIDIA Lightspeed Studios games.
- Portal: https://play.google.com/store/apps/details?id=com.nvidia.valvesoftware.portal&hl=en
- Doom3: https://play.google.com/store/apps/details?id=com.id.doom3bfg&hl=en
- Parallax: https://play.google.com/store/apps/details?id=com.nvidia.toastygames.parallax&hl=en
 
Last edited:

sorex

Expert
Licensed User
Longtime User
yes, I meant the second one. if there are only games that are playable with such controller.

Doom3 makes sense, you already need half a keyboard to play such games these days.
 

agraham

Expert
Licensed User
Longtime User
I am resurrecting this old thread to notify an improvement that might be of interest. I have been playing with the idea of using a game controller instead of on-screen buttons to control an app so giving more screen space for information. You get seventeen buttons and four joystick axes to use and it could be especially convenient with a controller that holds the device like this one.
s-l1600.jpg


While this demo works fine for the older Dinput type joystick where the Dpad buttons are reported via KeyPress events, it does not report the Dpad buttons for Xinput (Xbox 360) joysticks where the Dpad is reported like an analogue joystick through motion events. Remediating this is fortunately very simple.

B4X:
'In Gamepad.Class_Globals add two variables to hold the Dpad state

Dim Hat_X As Float
Dim Hat_Y As Float


' In Main.Activity_OnGenericMotionEvent add these lines to query the Dpad state
Controller.Hat_X = Controller.getAxisValue(Controller.AXIS_HAT_X)
Controller.Hat_Y = Controller.getAxisValue(Controller.AXIS_HAT_Y)


'In Main_Cycle_Tick insert these lines to the Output.Text string after "R Trigger: " ... & _
"DPAD X: " & Round(Controller.Hat_X  * 100) & "%" & CRLF & _
"DPAD Y: " & Round(Controller.Hat_Y  * 100) & "%" & CRLF & _
 

kimstudio

Active Member
Licensed User
Longtime User
@ilan Now there are two options in 150$ range:

Anbernic 505 and RetroPocket 3+: both are Android retrogame console,4G ram and 128G Rom, T610 CPU, can play anything emulated equal to/below PSP, PS1, MAME, SFC, MD, GBA...
The difference is 505 using PSV1000 OLED display 960*544 and RP3+ using iPhone 6s screen IPS 13xx*750?

I bought the Anbernic 505 from local online shopping for 127$, but I think it can be bought abroad as there are some review videos on youtube. You just search for these two brands in youtube or online shopping for information.

I would suggest to buy the RetroPocket3+ as in user group there are some feedbacks about quality of 505 although mine is fine. The PSV1000 OLED is color vivid but has some issue as they are too old. I think I will buy a later versoin of RetroPocket next time.

A photo from the user group who bought both:

rg.jpg
 

freedom2000

Well-Known Member
Licensed User
Longtime User
And you know what ?

Even DJI's motion controller is recognized with this App

motion controller.jpg


This little device is seen as a joystick and outputs Euler angles seen from a fixed referential "North East Down" : NED frame

JP
 
Top