Joystick detection : XBOX one controller not recognized

Hello,
I launched the TestJosticks test from JME tests, but I receive the message :
java.lang.IllegalStateException: Cannot find any joysticks!
I tested on Windows 10 and Linux.
For info, my gamepaf works with libGDX.
Do you have any idea ?

Did you enable the use of Joysticks?

public static void main(String[] args) {
        AppSettings settings = new AppSettings(true);
        settings.setUseJoysticks(true);
        
        Main app = new Main();
        app.setSettings(settings);
        app.start();

// More code
}

What happens when you print the names of the joysticks?

        Joystick[] joysticks = inputManager.getJoysticks();
        for(Joystick jst: joysticks){            
            System.out.println(jst.getName());           
        }

Which does tend to enable the joysticks:

ā€¦presuming something didnā€™t go wrong in launch.

TestJoysticks also dumps the joysticks.

I donā€™t have any specific help for you but I do feel like a similar question may have been asked here in the past.

I know people have managed to get some XBOX controllers working in JME because there have been other config tweaks, etc. related to that over the years.

In that light, if you do not find a better answer by searching the forum, it might be useful to know more specifics about which version of JME you are running, which version of JMEā€™s lwjgl support you have enabled, etcā€¦ and perhaps some more specifics about the controller you use. (I donā€™t knowā€¦ I guess there are differences in xbox controllers.)

welcome @takyon to JME!!

I want to ask a few quetions so we can help you with your issue

  • is your project using jme3-lwjgl3 or jme3-lwjgl?
  • Is your Xbox controller connected via USB of Wireless?
  • Do game clients like Steam recognize your gamepad? Note: this may be a stupid question but I never hooked up a gamepad to my PC before developing Depthris and I had some issues detecting it with JME
1 Like

Thanks [bloodwalker],
to answer yout questions :

I have this line in my gradle file :
implementation ā€œorg.jmonkeyengine:jme3-lwjgl3:$jmeVerā€
So I think I use jme3-lwjgl3.

I connect my controller via USB

Yes, I play with it to Steam or Epic games.
And as I said, it is recognized by libGDX.
I recently decided to port my game from libGDX to JME, so I have this gradle script

project.ext {
  jmeVer = '3.6.1-stable'
}

project(":assets") {
    apply plugin: "java"

    buildDir = rootProject.file("build/assets")

    sourceSets {
        main {
            resources {
                srcDir '.'
            }
        }
    }

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(17)
        }
    }
}

dependencies {

  // Core JME
  implementation "org.jmonkeyengine:jme3-core:$jmeVer"
  implementation "org.jmonkeyengine:jme3-desktop:$jmeVer"
  // Mac OS with LWJGL 3 doesn't allow AWT/Swing
  if (!System.getProperty("os.name").toLowerCase().contains("mac")) {
    implementation "org.jmonkeyengine:jme3-awt-dialogs:$jmeVer"
  }
  implementation "org.jmonkeyengine:jme3-lwjgl3:$jmeVer"

  // Suppress errors / warnings building in SDK
  implementation "org.jmonkeyengine:jme3-jogg:$jmeVer"
  implementation "org.jmonkeyengine:jme3-plugins:$jmeVer"
  
  // GUI Library
  implementation "com.simsilica:lemur:1.16.0"
  
  // Physics Library
  implementation "com.github.stephengold:Minie:8.0.0"
  
  // Networking Library
  implementation "com.github.tlf30:monkey-netty:0.1.1"

  // Additional Libraries
  implementation "org.jmonkeyengine:jme3-effects:$jmeVer"
  implementation "org.jmonkeyengine:jme3-terrain:$jmeVer"
  implementation "org.jmonkeyengine:jme3-testdata:$jmeVer"
  implementation "com.github.stephengold:Heart:9.0.0"
  implementation "com.epagagames:particlemonkey:1.1.0"
  implementation "com.github.polincdev:ShaderBlowEx:-SNAPSHOT"
  implementation "com.simsilica:sio2:1.8.0"
  implementation "com.simsilica:zay-es:1.5.0"
  implementation "com.simsilica:zay-es-net:1.5.2"
  // Assets sub-project
  runtimeOnly project(':assets')
}

jar {
    manifest {
        attributes 'Main-Class': application.mainClass
    }
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

wrapper {
    gradleVersion = '8.4'
}

[Pixelapp]as [pspeed] said, I tested TestJoysticks, which enables joysticks

settings.setUseJoysticks(true);

Thanks for your help :slight_smile:

1 Like

Ok, Looking at your gradle file it should be detected.

I modified the TestJoysticks code to try something else. The original code will throw an exception if no joysticks are connected when the app starts. I added a code to detect when it is connected after starting. so we can see if you get anything when you plug in your gamepad after the app starts.

package org.blocks;

import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.input.*;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

public class TestJoystickSimplified extends SimpleApplication {

    private Joystick viewedJoystick;
    private Node joystickInfo;
    private float yInfo = 0;

    public static void main(String[] args){
        TestJoystickSimplified app = new TestJoystickSimplified();
        AppSettings settings = new AppSettings(true);
        settings.setUseJoysticks(true);
        app.setSettings(settings);
        app.start();
    }

    @Override
    public void simpleInitApp() {
        getFlyByCamera().setEnabled(false);
        inputManager.addJoystickConnectionListener(connectionListener);

        // If there are no Joysticks connected when the application starts it will just print a message.
        Joystick[] joysticks = inputManager.getJoysticks();
        if (joysticks == null)
            System.out.println("Cannot find any joysticks!");

        try {
            PrintWriter out = new PrintWriter( new FileWriter( "joysticks-" + System.currentTimeMillis() + ".txt" ) );
            dumpJoysticks( joysticks, out );
            out.close();
        } catch( IOException e ) {
            throw new RuntimeException( "Error writing joystick dump", e );
        }

        joystickInfo = new Node( "joystickInfo" );
        joystickInfo.setLocalTranslation( 0, cam.getHeight(), 0 );
        guiNode.attachChild( joystickInfo );

        // Add a raw listener because it's easier to get all joystick events
        // this way.
        inputManager.addRawInputListener( new JoystickEventListener() );
    }

    protected void dumpJoysticks( Joystick[] joysticks, PrintWriter out ) {
        for( Joystick j : joysticks ) {
            dumpJoystick(j, out);
        }
    }

    protected void dumpJoystick( Joystick j, PrintWriter out ) {
        out.println( "Joystick[" + j.getJoyId() + "]:" + j.getName() );
        out.println( "  buttons:" + j.getButtonCount() );
        for( JoystickButton b : j.getButtons() ) {
            out.println( "   " + b );
        }

        out.println( "  axes:" + j.getAxisCount() );
        for( JoystickAxis axis : j.getAxes() ) {
            out.println( "   " + axis );
        }
    }

    protected void addInfo( String info, int column ) {

        BitmapText t = new BitmapText(guiFont);
        t.setText( info );
        t.setLocalTranslation( column * 200, yInfo, 0 );
        joystickInfo.attachChild(t);
        yInfo -= t.getHeight();
    }

    protected void setViewedJoystick( Joystick stick ) {
        if( this.viewedJoystick == stick )
            return;

        if( this.viewedJoystick != null ) {
            joystickInfo.detachAllChildren();
        }

        this.viewedJoystick = stick;

        if( this.viewedJoystick != null ) {
            // Draw the hud
            yInfo = 0;

            addInfo(  "Joystick:\"" + stick.getName() + "\"  id:" + stick.getJoyId(), 0 );

            yInfo -= 5;

            float ySave = yInfo;

            // Column one for the buttons
            addInfo( "Buttons:", 0 );
            for( JoystickButton b : stick.getButtons() ) {
                addInfo( " '" + b.getName() + "' id:'" + b.getLogicalId() + "'", 0 );
            }
            yInfo = ySave;

            // Column two for the axes
            addInfo( "Axes:", 1 );
            for( JoystickAxis a : stick.getAxes() ) {
                addInfo( " '" + a.getName() + "' id:'" + a.getLogicalId() + "' analog:" + a.isAnalog(), 1 );
            }

        }
    }

    /**
     * Listener when a joystick connects/disconnects
     */
    private JoystickConnectionListener connectionListener = new JoystickConnectionListener() {

        @Override
        public void onConnected(Joystick joystick) {
            System.out.println("Joystick connected: " + joystick.getName());

            try {
                PrintWriter out = new PrintWriter( new FileWriter( "joysticks-" + System.currentTimeMillis() + ".txt" ) );
                dumpJoystick(joystick, out);
            } catch (IOException e) {
                throw new RuntimeException( "Error writing joystick dump", e );
            }
        }

        @Override
        public void onDisconnected(Joystick joystick) {
            System.out.println("Joystick disconnected: " + joystick.getName());
        }
    };

    /**
     *  Easier to watch for all button and axis events with a raw input listener.
     */
    protected class JoystickEventListener implements RawInputListener {

        final private Map<JoystickAxis, Float> lastValues = new HashMap<>();

        @Override
        public void onJoyAxisEvent(JoyAxisEvent evt) {
            Float last = lastValues.remove(evt.getAxis());
            float value = evt.getValue();

            // Check the axis dead zone.  InputManager normally does this
            // by default but not for raw events like we get here.
            float effectiveDeadZone = Math.max(inputManager.getAxisDeadZone(), evt.getAxis().getDeadZone());
            if( Math.abs(value) < effectiveDeadZone ) {
                if( last == null ) {
                    // Just skip the event
                    return;
                }
                // Else set the value to 0
                lastValues.remove(evt.getAxis());
                value = 0;
            }
            setViewedJoystick( evt.getAxis().getJoystick() );
            //gamepad.setAxisValue( evt.getAxis(), value );
            if( value != 0 ) {
                lastValues.put(evt.getAxis(), value);
            }
        }

        @Override
        public void onJoyButtonEvent(JoyButtonEvent evt) {
            setViewedJoystick( evt.getButton().getJoystick());

            if(evt.isPressed()) {
                System.out.println(evt.getButton().getButtonId());
            }
        }

        @Override
        public void beginInput() {}
        @Override
        public void endInput() {}
        @Override
        public void onMouseMotionEvent(MouseMotionEvent evt) {}
        @Override
        public void onMouseButtonEvent(MouseButtonEvent evt) {}
        @Override
        public void onKeyEvent(KeyInputEvent evt) {}
        @Override
        public void onTouchEvent(TouchEvent evt) {}
    }
}

Hello, sorry for late answer, I didnā€™t have much time to work on my project.

I donā€™t know what happened, but now, my controller works in Linux.
Now, Iā€™m several several problems, but letā€™s begin with this one :

When I implement RawInputListener, and the method :slight_smile: public void

onJoyAxisEvent(JoyAxisEvent evt) {

the call to evt.getJoyIndex() returns two different indexes, while I have only one controller plugged. Is the keyboard considered as a joystick some way ?

Hi, little up of my question ^^

Some ā€˜eventā€™ happened. If it were me, Iā€™d dump all of the information about the event and notice what I was doing when the event happened.

Your joystick likely has several axes. Left/right for both sticks. Up/down for both sticks. Usually at least one axis (sometimes two) for the bottom triggers.

Edit: and yes, while Iā€™d expect all joystick events from the same joystick to have the same ā€œjoyIndexā€, itā€™s not necessarily true. You also may have some other device with an axis on it.

ā€¦something on the event may provide more information since there are also ā€˜logical IDsā€™ for things.

1 Like

Ok, there were actualy my laptop keyboard having axis.

To detect only real josticks, i use this code

		Joystick playerJoystick = null;
		Joystick[] joysticks = getInputManager().getJoysticks();
		if (joysticks != null) {
			for (Joystick joystick : joysticks) {
				if (joystick.getAxes().size() > 2) {
					playerJoystick = joystick;
				}
			}
			player = new Player(inputListener = new JoystickAndKeyboardEventListener(playerJoystick,
					getInputManager().getAxisDeadZone()), character);
			getInputManager().addRawInputListener(inputListener);
		}
1 Like