Search code examples
java3djmonkeyengine

How to listen for mouse movements correctrly in JME3?


I wish geometry to rotate around it's center by mouse movement.

Similar like camera rotates in sample application.

The program below should rotate the cube on mouse moves horizontally. If mouse moves left, it should rotate left, if mouse moves right, it should rotate right.

Unfortunately, it always rotate right.

package jme3test.helloworld;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jme3.app.SimpleApplication;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.math.ColorRGBA;

public class HelloJME3_3 extends SimpleApplication {

    private static final Logger log = LoggerFactory.getLogger(HelloJME3_3.class);

    public static void main(String[] args){
        HelloJME3_3 app = new HelloJME3_3();
        app.start(); // start the game
    }

    @Override
    public void simpleInitApp() {
        Box b = new Box(1, 1, 1); // create cube shape
        final Geometry geom = new Geometry("Box", b);  // create cube geometry from the shape
        Material mat = new Material(assetManager,
          "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
        mat.setColor("Color", ColorRGBA.Blue);   // set color of material to blue
        geom.setMaterial(mat);                   // set the cube's material
        rootNode.attachChild(geom);              // make the cube appear in the scene


        flyCam.setEnabled(false); 

        inputManager.addMapping("RotateX", 
                new MouseAxisTrigger(MouseInput.AXIS_X, false),
                new MouseAxisTrigger(MouseInput.AXIS_X, true)
        );

        inputManager.addListener(new AnalogListener() {

            @Override
            public void onAnalog(String name, float value, float tpf) {
                log.debug(name);

                if( "RotateX".equals(name) ) {
                    //geom.rotate((float) (value*speed), 0, 0);
                    geom.rotate(0, (float) (value*speed), 0);

                }
            }
        }, "RotateX");



    }
}

Solution

  • The problem here is that both positive and negative axis movements generate positive values for value. Hence your object only rotates one way.

    The simplest solution to this is to bind positive and negative axis movements separately and manage them separately.

    public class HelloJME_3 extends SimpleApplication {
    
    
        public static void main(String[] args){
            HelloJME_3 app = new HelloJME_3();
            app.start(); // start the game
        }
    
        @Override
        public void simpleInitApp() {
            Box b = new Box(1, 1, 1); // create cube shape
            final Geometry geom = new Geometry("Box", b);  // create cube geometry from the shape
            Material mat = new Material(assetManager,
              "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
            mat.setColor("Color", ColorRGBA.Blue);   // set color of material to blue
            geom.setMaterial(mat);                   // set the cube's material
            rootNode.attachChild(geom);              // make the cube appear in the scene
    
    
            flyCam.setEnabled(false); 
    
            inputManager.addMapping("RotateX", 
                    new MouseAxisTrigger(MouseInput.AXIS_X, true)
            );
    
            inputManager.addMapping("RotateX_negative", 
                    new MouseAxisTrigger(MouseInput.AXIS_X, false)
            );
    
    
            inputManager.addListener(new AnalogListener() {
    
                @Override
                public void onAnalog(String name, float value, float tpf) {
    
                    if( "RotateX".equals(name) ) {
                        geom.rotate(0, (float) (value*speed), 0);
    
                    }else if("RotateX_negative".equals(name)){
                        geom.rotate(0, (float) (-value*speed), 0);
                    }
                }
            }, "RotateX", "RotateX_negative");
    
    
        }
    }
    

    Base rotation on absolute cursor position

    You mentioned that you would like to create a rotation such that when the mouse is in the centre of the screen the object is unrotated. You can achieve this using the mouses screen coordinates.

    inputManager.getCursorPosition();
    

    This of course also requires you to set the rotation rather than rotating in addition to the current rotation (this takes a Quaternion argument but we largely don't have to worry about how that works.

      Quaternion quat=new Quaternion();
      quat.fromAngles(0, desiredAngle, 0);
    
      geom.setLocalRotation(quat);
    

    Of course when the mouse is initially captured the object will suddenly move to its "correct" position for wherever the mouse now is.

    Complete code

    public class HelloJME_3 extends SimpleApplication {
    
        public static float PIXELSMOVED_TO_RADIANSROTATED=0.01f;
    
    
        public static void main(String[] args){
            HelloJME_3 app = new HelloJME_3();
            app.start(); // start the game
        }
    
        @Override
        public void simpleInitApp() {
            Box b = new Box(1, 1, 1); // create cube shape
            final Geometry geom = new Geometry("Box", b);  // create cube geometry from the shape
            Material mat = new Material(assetManager,
              "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
            mat.setColor("Color", ColorRGBA.Blue);   // set color of material to blue
            geom.setMaterial(mat);                   // set the cube's material
            rootNode.attachChild(geom);              // make the cube appear in the scene
    
    
            flyCam.setEnabled(false); 
    
            inputManager.addMapping("MouseMoved", 
                    new MouseAxisTrigger(MouseInput.AXIS_X, false),
                    new MouseAxisTrigger(MouseInput.AXIS_X, true)
            );
    
            inputManager.addListener(new AnalogListener() {
    
                @Override
                public void onAnalog(String name, float value, float tpf) {
                    inputManager.getCursorPosition();
    
                        float centredX=inputManager.getCursorPosition().x-0.5f*settings.getWidth();
    
                        Quaternion quat=new Quaternion();
                        quat.fromAngles(0, PIXELSMOVED_TO_RADIANSROTATED*centredX, 0);
    
                        geom.setLocalRotation(quat);
    
                }
            }, "MouseMoved");
        }
    }