InputMapper axis mistake

I want to use InputMapper for my game, but probably I’ve missed something in that I go up and never down.
This is my code:

    public static final String GROUP_MOVEMENT = "group movement";
    
    public static final FunctionId F_MOVE = new FunctionId(GROUP_MOVEMENT, "Move");

        //inputMapper.map(F_MOVE, InputState.Negative, Axis.JOYSTICK_LEFT_Y);
        inputMapper.map(F_MOVE, KeyInput.KEY_UP);
        inputMapper.map(F_MOVE, InputState.Negative, KeyInput.KEY_DOWN);
        inputMapper.addStateListener(this, F_MOVE);

    @Override
    public void valueChanged(FunctionId fi, InputState is, double d) {
        if (fi == F_MOVE) {
            if (is == InputState.Positive) {
                upPressed = true;
                downPressed = false;
            } else if (is == InputState.Negative) {
                upPressed = false;
                downPressed = true;
            } else {
                upPressed = false;
                downPressed = false;
            }
        }
    }

Thanks!

Have you printed what the actual state is? Hard to tell what is going on from the code provided.

I assume you’ve looked at the camera movement Lemur Gem.

InputState is positive when I press “up” and also when I press “down”.

Then there is something else going on in code that I can’t see. I assume you’ve taken your code from here:

…but you might want to put together a simple test case that illustrates the issue as the lemur gem 1 has always worked. (And indeed my own games would break hard if this was broken.)

so it must be something outside of what you’ve posted.

Also, if you have code the writes debug output then include it in what you post.

Here is a simple self contained test. Pressing up or down will yeld the same result.

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.input.KeyInput;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.input.FunctionId;
import com.simsilica.lemur.input.InputMapper;
import com.simsilica.lemur.input.InputState;
import com.simsilica.lemur.input.StateFunctionListener;


public class Test extends SimpleApplication{
    public static final String GROUP_MOVEMENT = "group movement";
    public static final FunctionId F_MOVE = new FunctionId(GROUP_MOVEMENT, "Move");
    public static void main(String args[]){
        new Test().start();
    }
    
    @Override
    public void simpleInitApp() {
        GuiGlobals.initialize(this);
        stateManager.attach(new MovementAppState());
    }
class MovementAppState extends BaseAppState implements StateFunctionListener{

    public static final String GROUP_MOVEMENT = "group movement";
    
    InputMapper inputMapper;
    

    
    @Override
    protected void initialize(Application app) {
        inputMapper = GuiGlobals.getInstance().getInputMapper();
        //inputMapper.map(F_MOVE, InputState.Negative, Axis.JOYSTICK_LEFT_Y);
        inputMapper.map(F_MOVE, KeyInput.KEY_UP);
        inputMapper.map(F_MOVE, InputState.Negative, KeyInput.KEY_DOWN);
        inputMapper.addStateListener(this, F_MOVE);
    }
    

    
    @Override
    public void update(float tpf) {
    }    
    
    @Override
    public void valueChanged(FunctionId fi, InputState is, double d) {
        System.out.println(is+"..........................");
    }

    @Override
    protected void cleanup(Application app) {
    }

    @Override
    protected void onEnable() {
        inputMapper.activateGroup(GROUP_MOVEMENT);
    }

    @Override
    protected void onDisable() {
        inputMapper.activateGroup(GROUP_MOVEMENT);
    }
    
}    
}

I have to add, I’m using latest Lemur release from Github.

Thanks for the details. I will try to look at this later today after I get some sleep.

Does the Lemur gem I linked to work for you?

The gem worked by itself, but uses Mouse mapping and is a slightly different use case…

It maps WASD the exact same way you do with the cursor keys. The only difference I see is that it uses an analog listener for movement instead of a state listener. (which makes more sense but it would be good to know if one way isn’t working)

Oh, I see… I used the gem back with my OTHER game, and used for movement+camera control. So yeah, the problem might be the analoglistener vs the statelistener.

Tried swapping up/down with W/S but the result is the same.

Alright, that’s it. StateListener DO NOT have the “Negative” Inputstate: it’s either Positive or neutral. So in my test case the “InputState.Negative” was simply ignored.

Is it by design or is it a bug?

Another weird thing: I’ve replaced the key input with mouse input and I never get the ‘0’ (neutral) inputstate.

Test case:

public class Test extends SimpleApplication{
    public static final String GROUP_MOVEMENT = "group movement";
    public static final FunctionId F_MOVE = new FunctionId(GROUP_MOVEMENT, "Move");
    public static void main(String args[]){
        new Test().start();
    }
    
    @Override
    public void simpleInitApp() {
        GuiGlobals.initialize(this);
        stateManager.attach(new MovementAppState());
    }
class MovementAppState extends BaseAppState implements AnalogFunctionListener{

    public static final String GROUP_MOVEMENT = "group movement";
    
    InputMapper inputMapper;
    

    
    @Override
    protected void initialize(Application app) {
        inputMapper = GuiGlobals.getInstance().getInputMapper();
        inputMapper.map(F_MOVE, InputState.Negative, Axis.MOUSE_Y);
        inputMapper.addAnalogListener(this, F_MOVE);
    }
    

    
    @Override
    public void update(float tpf) {
    }    
    

    @Override
    protected void cleanup(Application app) {
    }

    @Override
    protected void onEnable() {
        inputMapper.activateGroup(GROUP_MOVEMENT);
    }

    @Override
    protected void onDisable() {
        inputMapper.activateGroup(GROUP_MOVEMENT);
    }

        @Override
        public void valueActive(FunctionId fi, double d, double d1) {
        System.out.println(d+".........................."+d1);
        }
    
}    
}

Probably a bug. I will have to look deeper. If you are feeling especially generous then maybe you can file an issue in the tracker on the Lemur github project.

For your use-case, it seems like analog listener is more what you want anyway, yes? (Just noticed you are setting boolean flags… which are bound to be used somewhere else in update())

The mouse is never neutral. There is no ‘center position’ on a mouse.

1 Like

I do!

Yes, the analog listener is probably what I want.

How do I know that the mouse is not moving? I guess when I don’t see events the mouse is not moving. But since the underlying philosophy here is “you get an event when the state changes”, I thought that an event “Mouse stopped” should be fired… or not?

How long do I wait before deciding that the mouse has stopped? Every other axis type has a neutral position but the mouse doesn’t. Any way I pick to do it would be making too many assumptions that it’s easier for the app to handle.

Besides, I really tend to hate interfaces that try to treat the mouse like a joystick… just nasty.

1 Like

How do I enable gamepad/joystick support on Lemur?

Enable it in JME.

1 Like

Now I’m trying to add combo support to Lemur http://wiki.jmonkeyengine.org/doku.php/jme3:advanced:combo_moves

I’ve see that it requires some rework, have you already looked into this @pspeed?

Rework where? To the combo example or to Lemur?

In my opinion, Lemur isn’t where combos should be supported (as in sequence of actions). That’s a code thing. However, Lemur will already let you detect simultaneous combo keys which should make the rest easier.

Edit: Actually, I guess you could pretty easily write an app state that would accumulate combos as lists of function IDs (if properly registered with Lemur) and then once a certain set was matched could forward to a regular state listener with a custom function ID. Still not part of Lemur but could reuse a lot of lemur’s stuff. The app state would then clear the set after a certain amount of time without having received a new event. That would be your internal combo interval.

1 Like