Problems with analog listener

I am trying to use analog listener to control a car’s movement. I want to use analog listener, in order to calculate different acceleration force at different speed.

[java]

if(name.equals(“Ups”)){

player.accelerate();

}else{

player.stopAccelerate();

}[/java]



It is supposed the vehicle accelerates when I press UP key, and stops accelerating if I don’t press it. Sometimes it works well.

But it happens sometimes when I release the UP key, the console output is like the following and the car will keep going forward.

accelerate:-807.36945

accelerate:-806.16345

accelerate:-806.16345

stop accelerate:793.83655

accelerate:-806.16345



And another problem is the value I got from “onAnalog(String name, float value, float tpf)” seems to be a constant which doesn’t change even I press the key quite long.



Thanks very much for any help and suggestion.

There is a boolean passed int the on Analog function that is true when the button is pressed and false when it’s release.

Try to use it and see if it change something



if(name.equals(“Ups”) && isKeyPressed)){



The “value” is the time the key in seconds the key was pressed in the frame. If you hold the key, and your framerate is constant, then the value will be constant as well.

nehon said:
There is a boolean passed int the on Analog function that is true when the button is pressed and false when it's release.
Try to use it and see if it change something
`
if(name.equals("Ups") && isKeyPressed)){
....
`


I cannot find a boolean called isKeyPressed in onAnalog().

There is the declaration of AnalogListener:
[java]
public interface AnalogListener extends InputListener {
public void onAnalog(String name, float isPressed, float tpf);
}[/java]

For ActionListener, there is a boolean to tell if the key is pressed.
[java]public interface ActionListener extends InputListener {
public void onAction(String name, boolean isPressed, float tpf);
}[/java]

If I made anything wrong, please tell me.
1 Like

mhh you’re right, i’m wrong, the boolean is for the ActionListener.



ok i think i understand what’s going on.

Your code of your first post can’t work properly, the else part can’t be executed when you release the button.

keep this



if(name.equals(“Ups”)){

player.accelerate();

}



in the analog listener

and add this in an action listener



if(name.equals(“Ups”) && !isPressed){

player.stopAccelerate();

}



don’t forget to register “Ups” in your action listener

the analog listener will be continuously called while the button is pressed, and the action listener will catch the release of the key.

nehon said:
mhh you're right, i'm wrong, the boolean is for the ActionListener.

ok i think i understand what's going on.
Your code of your first post can't work properly, the else part can't be executed when you release the button.
keep this
`
if(name.equals("Ups")){
player.accelerate();
}
`
in the analog listener
and add this in an action listener
`
if(name.equals("Ups") && !isPressed){
player.stopAccelerate();
}
`
don't forget to register "Ups" in your action listener
the analog listener will be continuously called while the button is pressed, and the action listener will catch the release of the key.


Thank you very much for following this, nehon.

I tried your suggestion. SOMETIMES it works, but SOMETIMES i got the situation like this
accelerate (i keep pressing UP key)
accelerate
accelerate (i release the key here)
stop accelerate (actionListener is triggered)
accelerate (the analogListener gets executed once more)

My guess is, the moment when I release the UP key, the program has already triggered one loop of analogListener. Thus even the releasing of the key triggers my actionListener, the analogListener has to finish the current loop and in that loop the UP key is set as pressed. So the accelerate method gets excuted once more.

like,
[java]
public void onAnalog(String name, float value, float tpf) {
if(name.equals("Ups")){ //if I release the UP key here, the actionListener is triggered,
//but the accelerate method has to be get executed once more.
player.accelerate(); //if I release the UP key after this line, the accelerate() wont get executed and everything is fine...
}else if(name.equals("Downs")){
player.brake();
}
}[/java]

The reason why I use analogListener is that accelerate() is supposed to run continuously to calculate acceleration force according to current speed (make sure the speed won't get higher than top speed, and also make the car harder to accelerate under higher speed). But once the bug is triggered, my car will accelerate in an constant accelerated speed, and finally exceed the speed limit i set to it.

Is there any solution to this problem?

I don’t know if that could change anything, but, try to register your ActionListener AFTER you AnalogListener.

The events should be fired in sequence, and your ActionListener would be triggered in the end.

I had same problem…

AnalogListener is little like simpleUpdate > code are done again and again.

Bettet way can be >



[java]

private ActionListener actionListener = new ActionListener()

{ public void onAction(String name, boolean keyPress, float ptf)

{ if(name.equals("Down")&&!keyPress)//button UP

{ stop();

}

if(name.equals("Down")&&keyPress)//button DOWN

{ start();

}

}}}}}}}}}}}}}}}};

[/java]

Out of interest - does anyone know why the analogue listener doesn’t get an event when the key is released? I know I’ve already run into the situation where I’ve needed both an action and an analogue listener for just this reason…

@zarch said:
Out of interest - does anyone know why the analogue listener doesn't get an event when the key is released? I know I've already run into the situation where I've needed both an action and an analogue listener for just this reason...

What situation?
The AnalogListener is invoked only if the input trigger has a non-zero value
@Momoko_Fan said:
What situation?
The AnalogListener is invoked only if the input trigger has a non-zero value


A situation where I need to know both how long something has been pressed for (i.e. analogue listener) and when it is released (action listener).

For example click handling - I want to respond differently to single click (select) and click-hold (menu), but on the click-hold I don't want to wait until they release I want to respond as soon as the threshold is reached.

If I do that using an analogue listener then I don't get notified when they release the key so single click doesn't happen. If I use an action listener then I only get notified when they release - so the menu click doesn't trigger until they release.

I could fudge it using an action listener and checking in the update() but that's still requiring two places (action+update) and is less clean (as update might end up checking multiple things) than action+analogue.

I don’t understand why having a listener that is both action and analog is bad. I do this all the time. In fact, I think every listener that isn’t a RawInputListener in my app is analog+action.

Because if you are having to do both action and analog all the time it suggests that the design of the listeners as it stands could be improved :slight_smile:



Actually from the docs I wasn’t sure if doing one listener that is both action and analogue would work. How does JME3 know which you are adding and which order to call the methods in? I haven’t got around to testing it but based on the lack of documentation on doing so I figured it was best avoided…

@zarch said:
Because if you are having to do both action and analog all the time it suggests that the design of the listeners as it stands could be improved :)

Actually from the docs I wasn't sure if doing one listener that is both action and analogue would work. How does JME3 know which you are adding and which order to call the methods in? I haven't got around to testing it but based on the lack of documentation on doing so I figured it was best avoided....


Because you add the same listener both ways.

This pattern is sooooooo common with mouse listening in Swing that I don't even give it a second thought. You are placing arbitrary and unnecessary design restrictions on what is "good" in this case, I think.

In swing you add the various types of mouse etc listeners using separate addMouseListener/addActionListener etc methods. Also (in my experience) most listeners are anonymous inner classes and therefore only can implement one interface.



I’d sort of assumed that analog and action listener were separate and there was two overloaded methods which would make it physically impossible to use a combined one. Now I’ve looked more closely at the code though I see they each share another interface and you are adding that… I didn’t see anything about doing so in the docs though.



I still don’t see where its specified what order the action and analogue methods are called when combining them - or indeed any mention in the relevant tutorial that it’s even possible and/or recommended to combine them like that.

@zarch said:
I'd sort of assumed that analog and action listener were separate and there was two overloaded methods which would make it physically impossible to use a combined one. Now I've looked more closely at the code though I see they each share another interface and you are adding that... I didn't see anything about doing so in the docs though.

I still don't see where its specified what order the action and analogue methods are called when combining them - or indeed any mention in the relevant tutorial that it's even possible and/or recommended to combine them like that.

See javadoc for InputManager.addListener():
Adds a new listener to receive events on the given mappings.

The given InputListener will be registered to receive events
on the specified mapping names. When a mapping raises an event, the
listener will have its appropriate method invoked, either
ActionListener#onAction() or AnalogListener#onAnalog()
depending on which interface the listener implements.
If the listener implements both interfaces, then it will receive the
appropriate event for each method.

That is not mentioned anywhere in the tutorial though - and it still doesn’t say what order they are called in, whether that order is fixed, and whether that order can be relied on not to change if someone changes the relevant code in the library.

Now it is mentioned in the tutorial.



If in doubt, don’t rely on the order. Its designed to be that way.

You can deal with the results later in an AppState’s update method.

1 Like

Thanks, that’s clearer.

I had this issue as well.



I wanted to implement walking procedurally, walking while the user held down the walk key, as with an analogue listener, but when the player released the key, I wanted to return to standing position (rather than having the character in some weird position).



My solution was to have an action listener inform my objects when the key was pressed and released, store that information in boolean variable(s), and then handle the analog part in the objects’ update methods.



edit: oops, I see this was already answered…

2 Likes