There are some problems with InputMapper

MouseInputActions.java

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package CameraAndMouse.MouseFiniteStateMachine;

import com.jme3.math.Vector3f;
import com.simsilica.lemur.input.AnalogFunctionListener;
import com.simsilica.lemur.input.FunctionId;
import com.simsilica.lemur.input.InputState;
import com.simsilica.lemur.input.StateFunctionListener;
import java.util.HashMap;
import java.util.List;

/**
 *鼠标的状态
 *
 * @author icyboxs
 */
public class MouseInputActions implements StateFunctionListener, AnalogFunctionListener{
    InputState inputState;
    @Override
    public void valueChanged(FunctionId func, InputState value, double tpf) {
        
        if(func == MouseInputMap.Left_MOUSE_CLICK && value == InputState.Positive){
                System.out.println(value+",Message printed to console.  tpf was: " + tpf);
                inputState=InputState.Positive;
        }else{
            System.out.println(value+",Message printed to console.  tpf was: " + tpf);
        inputState=value;
        }
        
    }

    @Override
    public void valueActive(FunctionId func, double value, double tpf) {
        System.err.println(func+","+value);
    }

    
}

MouseInputMap.java

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package CameraAndMouse.MouseFiniteStateMachine;

import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.simsilica.lemur.input.Axis;
import com.simsilica.lemur.input.Button;

import com.simsilica.lemur.input.FunctionId;
import com.simsilica.lemur.input.InputMapper;

/**
 *
 * @author icyboxs
 */
public class MouseInputMap {
    public static final String MOUSE_GROUP = "MOUSE_GROUP";

    
    public static final FunctionId Left_MOUSE_CLICK = new FunctionId(MOUSE_GROUP, "LeftMouseClick");
    public static final FunctionId Right_MOUSE_CLICK = new FunctionId(MOUSE_GROUP, "Right_MOUSE_CLICK");
    public static final FunctionId MOVE_MOUSE_Y = new FunctionId(MOUSE_GROUP, "MOVE_MOUSE_Y");
    public static final FunctionId MOVE_MOUSE_X = new FunctionId(MOUSE_GROUP, "MOVE_MOUSE_X");
    
    
    public static void initializeDefaultMappings( InputMapper inputMapper ) {
        // Default key mappings
        inputMapper.map(Left_MOUSE_CLICK, Button.MOUSE_BUTTON1);
        inputMapper.map(Right_MOUSE_CLICK, Button.MOUSE_BUTTON2);
        inputMapper.map(MOVE_MOUSE_Y, Axis.MOUSE_X);
        inputMapper.map(MOVE_MOUSE_X, Axis.MOUSE_Y);
    }    
}

This is a piece of pseudo-code that indicates that I have initialized 2 ways of listening to the mouse at the same time.

protected void initialize(Application aplctn) {
  CursorEventControl.addListenersToSpatial(simpleApp.getRootNode().getChild("MapNode"), new DefaultCursorListener() {

            @Override
            protected void click(CursorButtonEvent event, Spatial target, Spatial capture) {
//        System.out.println("我被点击了:" + target);
//        System.out.println("我被点击了:" + capture);
                System.out.println("我被点击了:" + event.isPressed());
//        System.out.println(results);

                // System.out.println(scenarioState.mapGrid3D.getCellsizePosition(results.getContactPoint()));
            }

            @Override
            public void cursorButtonEvent(CursorButtonEvent event, Spatial target, Spatial capture) {
                event.setConsumed();

                if (event.isPressed()) {
                    xDown = event.getX();
                    yDown = event.getY();
                    System.err.println(results.getContactPoint());
                    //BOX(scenarioState.mapGrid3D.getPutPlaceBuildingCoordinates(results.getContactPoint(),new Vector2f(3,3)));//向网格内添加建筑
                    // 鼠标按下,记录起始位置
                        startClick.set(inputManager.getCursorPosition());
                    isSelecting = true;
                } else {
                    float x = event.getX();
                    float y = event.getY();
                    //它检查鼠标在释放按钮时是否移动得很小(小于3个像素)。如果是,它调用 click 方法
                    if (Math.abs(x - xDown) < 3 && Math.abs(y - yDown) < 3) {
                        click(event, target, capture);
                    }
                    isSelecting = false;
                    startClick.zero();
                    endClick.zero();
                    updateSelectionBox();
                }
            }

            @Override
            public void cursorEntered(CursorMotionEvent event, Spatial target, Spatial capture) {
                // System.err.println(event);//.getCollision().getContactPoint()
            }

            @Override
            public void cursorExited(CursorMotionEvent event, Spatial target, Spatial capture) {

            }

            @Override
            public void cursorMoved(CursorMotionEvent event, Spatial target, Spatial capture) {
                results = event.getCollision();
                if (isSelecting) {
                    endClick.set(inputManager.getCursorPosition());
                    // 更新选择框大小和位置
                    updateSelectionBox();

                }
            }
        });


    GuiGlobals.initialize(simpleApp);
    inputMapper = GuiGlobals.getInstance().getInputMapper();
   // inputMapper.map(F_X_ROTATE_MOUSE, Axis.MOUSE_X);
   MouseInputMap.initializeDefaultMappings(inputMapper);
    MouseInputActions mouseInputActions = new MouseInputActions();
    inputMapper.addStateListener(mouseInputActions,MouseInputMap.Left_MOUSE_CLICK);
    inputMapper.addAnalogListener(mouseInputActions, MouseInputMap.Left_MOUSE_CLICK);
    inputMapper.activateGroup(MouseInputMap.MOUSE_GROUP);  

}

Something happens to inputMapper.addAnalogListener when both types of listeners are present at the same time.

This video shows how lifting the mouse when I press the left mouse button and dragging into the terrain inputMapper.addAnalogListener seems to conflict with CursorEventControl.addListenersToSpatial even though my mouse state is off inputMapper. addAnalogListener is still called.

I have to click the left mouse button again outside of the map (i.e., where CursorEventControl.addListenersToSpatial can’t detect it) in order to undo the call to inputMapper.addAnalogListener

Is this how lemurs were originally designed? Or am I using this class incorrectly?

I’d have to dive into the code to know for sure but it’s possible that listeners added to spatials take precedence over the raw axis listeners. They will mark the event as consumed and then it won’t get delivered further.

…this may also happen with listeners on JME’s InputManager since it should also be paying attention to the ‘consumed’ flag.

But also I have trouble following what your code is trying to do. It may be a little too early in the morning to me. You seem to have a lot of drag-box handling right on the spatial instead of in the more global analog listeners and I’m not sure how that can ever work. Are you trying to only drag the box when it’s over the map node? Or?

I want to use mouse listening globally in order to draw multiselect boxes, what should I do? Instead of using Lemur, should I use jme’s AnalogListener for global settings?

If you map has a background then you should be able to register a listener with that, too.

The problem with trying to help with stuff that is essentially a black screen with two dots on it is that it’s very hard to imagine what things are supposed to do and look like.

I think you may be talking about the skybox, yes I searched the forums for this method but I’m not sure this is a good solution for me, I’ll get back to you after trying it out

You’re right I just tried InputManager and had the same problem!

The red box is outside the map node I stand-alone trigger mouse down and move the mouse to the map node to release the left mouse button, will find no trigger off and false, when I move the mouse to the map node again outside and re-stand-alone left mouse button everything is back to normal.

I’m guessing that CursorEventControl.addListenersToSpatial seems to be triggering something early to cause these problems, but I have a simple guess that releasing the left mouse button in the map node is causing other listeners to not receive the signal that the mouse has been released.

… Well, actually, you’ve said it all.

I just noticed. :sweat_smile: :sweat_smile: :sweat_smile: :sweat_smile:

No. I’m talking about the “map”.

I have no idea what your game is.
I have no idea what your user interface will look like.
I have no idea what the players will do when playing your game.

All I see is a black screen with a couple green things on it. I do not have enough imagination to make up the rest.

Is there a map? Or are the objects floating in space?

Will you eventually actually have a map… like roads, hills, valleys? Or is your game set in the void of empty space?

If you have a map… like a big giant quad (not a sky box… but the ground) or something then you can listen on that.

It’s really rare to have a “map” that is simply “nothing”.

I currently can’t provide you with the kind of perfect object displays you mentioned. That’s why I’m concerned I can’t describe the problem clearly. However, I guess you’ve already understood the issue I’m facing, you’re just not sure about what I want to do, and therefore, you can’t provide advice. What I mean is, I’d like to use both types of listeners, the raw listener and the spatial listener simultaneously. The spatial listener offers a lot of functionality and is very useful, while the raw listener needs to be used in non-spatial situations.


Took a nap, took a while, and I guess that’s probably why it was “consumed” too early, preventing it from passing on.

Well to describe what I wanted to do I fired up a game of Starcraft II.
I need to use the initial listener so that I can draw such a dragging checkbox anywhere in the GUI.

Currently I can only draw drag checkboxes like this in MapNode, which is not what I want so I started to use the initial listener, then I realized that the spatial listener would ‘consumed’ prematurely so currently I’m thinking about how to solve this problem, do you have any suggestions?

Currently, I can only draw drag checkboxes in the map (the gray part indicated by the red arrow), I want to be able to draw drag checkboxes in the blue part (the blue line)

Put a big giant invisible quad where the blue part is. Then you can listen just like any other object.

What is supposed to be in the black space when your game is done?

Is it always going to be a big giant black void of mostly black on the screen?

I don’t need to see what it will look like… I just need to KNOW what it will look like.

It’s not clear that you really need two listeners. That’s why I was trying to figure out what you are actually trying to do.

Edit: Note regarding “big giant invisible quad”… even if you setCullHint(CullHint.Always), objects are still ‘picked’ by the mouse.

There’s nothing there.
At a later stage it could well be a skybox, but there’s nothing there at the moment

This part is just background.

NOT A SKYBOX. That’s not what I’m talking about. No skybox. Put skybox out of your head.

Something like:

Quad quad = new Quad(60000, 60000);
Geometry geom = new Geometry("clickPlane", quad);
geom.rotate(FastMath.QUARTER_PI, 0, 0);
geom.move(-30000, 0, -30000);
geom.setCullHint(CullHint.Always);
rootNode.attachChild(geom);

…there. Now you have clickable geometry even when you aren’t on “the map”.

That’s a good solution.

I’m still checking the code If I remove event.setConsumed(); I find that the spatial listener triggers 3 times when I press the mouse and three times when I lift it.

cursorButtonEvent:true
cursorButtonEvent:true
cursorButtonEvent:true
cursorButtonEvent:false
cursorButtonEvent:false
cursorButtonEvent:false

Give me some hints as to why this is happening?

 CursorEventControl.addListenersToSpatial(simpleApp.getRootNode(), new DefaultCursorListener() {

            @Override
            protected void click(CursorButtonEvent event, Spatial target, Spatial capture) {
            }

            @Override
            public void cursorButtonEvent(CursorButtonEvent event, Spatial target, Spatial capture) {
             
            //event.setConsumed();
                if (event.isPressed()) {
                    System.err.println("cursorButtonEvent:" + event.isPressed());
                    xDown = event.getX();
                    yDown = event.getY();
                    //System.err.println(results.getContactPoint());
                    //BOX(scenarioState.mapGrid3D.getPutPlaceBuildingCoordinates(results.getContactPoint(),new Vector2f(3,3)));//向网格内添加建筑
                    // 鼠标按下,记录起始位置
                        startClick.set(inputManager.getCursorPosition());
                    isSelecting = true;
                } else {
                    System.err.println("cursorButtonEvent:" + event.isPressed());
                    float x = event.getX();
                    float y = event.getY();
                    //它检查鼠标在释放按钮时是否移动得很小(小于3个像素)。如果是,它调用 click 方法
                    if (Math.abs(x - xDown) < 3 && Math.abs(y - yDown) < 3) {
                        click(event, target, capture);
                    }
                    isSelecting = false;
                    startClick.zero();
                    endClick.zero();
                    updateSelectionBox();
                }
                
            }

            @Override
            public void cursorEntered(CursorMotionEvent event, Spatial target, Spatial capture) {

            }

            @Override
            public void cursorExited(CursorMotionEvent event, Spatial target, Spatial capture) {

            }

            @Override
            public void cursorMoved(CursorMotionEvent event, Spatial target, Spatial capture) {
                results = event.getCollision();
                if (isSelecting) {
                    endClick.set(inputManager.getCursorPosition());
                    // 更新选择框大小和位置
                    updateSelectionBox();

                }
                
            }
        });

If I do this it seems to solve the “already consumed” problem, but it will cause problems with the space listener

:joy: :joy: :joy: :joy: :joy: :joy: :joy: :joy: :joy: :joy: :joy:

1 Like

Yeah, because you want to consume it but you don’t want to consume it. It’s going to confuse a lot of things. I haven’t looked enough into your code to figure out where you are registering listeners and if there is more than one that might be reporting that, etc… your debug output leaves out some information.

…but if I had to guess, because you add the listener to the root node then anything the mouse is over will get the button event. Since nothing consumes it then it’s going to be delivered to everyone of those things.

That’s what consuming is for. To keep it from continuing on through whatever you clicked.

The easiest approach is the one I’ve given already.

You have two choices basically:

  1. play nice with spatial-based listeners and use them for everything. (ie: put a ground plane)
  2. do not use any spatial-based listeners at all ever never never and always do your own basic mouse handling, picking, etc.) ie: stop using CursorEvents for anything.

(1) seems like the easier way to me. Since no doubt you will have some UI elements at some time and (2) becomes super hard in that case.

1 Like