scene2d: how to fire(event) and handle at scene/root? - java

i am trying to escalate touchDown and touchUp events from an actor Object to my applicationListener class. To do so, i called fire(event); in the InputListener of my Actor
this.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("Example", "touch started at (" + x + ", " + y + ")");
fire(event);
return true;
}
public void touchUp(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("Example", "touch ended at (" + x + ", " + y + ")");
}
});
To handle the event in my ApplicationListener class (which contains the stage of the actors), i added an InputListener to the stage
Gdx.input.setInputProcessor(stage);
stage.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("FIRE!!!", "I CAUGHT A FIRED EVENT!");
event.stop();
return true;
}
public void touchUp(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("FIRE!!!", "the fired event even touchupped.");
}
});
However when i touch my actor, i get a StackOverflowError as well as a ton of exceptions from several InputListeners (i assume that the event is not stopped properly and propagated to all actors in my scene). What am i missing here?
Also after firing the event i cant cast event.getTarget() (which is an Actor) to my Actor-Subclass anymore, which works just fine if i do this in the ActorSubclass itself. Meaning the following code creates an error when used in the ApplicationListener, but works in the MyActor class:
MyActor actor = (MyActor)event.getTarget();
Since the target is in fact a MyActor Object, how can i access it not only as Actor, but as MyActor?

As #chase said you are recursivelly calling the fire(even) method, as you can only set 1 InputProcessor (your stage) and so always the same method recieves the event.
You should instead use a InputMultiplexer:
add the Stage and your ApplicationListener as InputProcessor to the InputMultiplexer
Set the InputMulitplexer as the InputProcessor by calling Gdx.input.setInputProcessor(multiplexer);.
The InputMultiplexer will then give the event to the first InputProcessor and if it returns false, it will send the event to the next one.
So in your Stage you just need to return false.
But i don't understand why you are adding your Stage as a InputProcessor if you don't want it to handle inputs...
Anyways in scene2d you don't need to add an InputProcessor to the stage as it allready has one. Also the Actor allready has a boolean touchDown(float x, float y, int pointer) and other helpful methods.
A useful link: Scene2D wiki article

Aren't you just making a recursive call at this point by refiring the event every time you handle it?
Why not just return false from your listener?

Related

LibGDX TextButton - Any event getting called repeatedly as long as it's held down?

I am making a game where you have two buttons to rotate the player, one to left and the other one to right. I'm using a TextButton in LibGDX. My problem is that the method clicked(InputEvent event, float x, float y) in ClickListener is only called once it's clicked. I want an event to be called repeatedly as long as it's held down. Here is my code:
TextButton btnLeft = new TextButton("<", styleButton);
TextButton btnRight = new TextButton(">", styleButton);
btnLeft.setSize(100, 100);
btnRight.setSize(100, 100);
btnLeft.setPosition(25, 25);
btnRight.setPosition(200, 25);
btnLeft.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
player.rotate(-1);
System.out.println("Left");
}
});
btnRight.addListener(new ClickListener() {
Override
public void clicked(InputEvent event, float x, float y) {
player.rotate(1);
System.out.println("Right");
}
});
stage.addActor(btnLeft);
stage.addActor(btnRight);
The listener is not good place to perform continuous actions since it is by definition asynchronous mechanism. The place to perform some action like this is render() method of Screen or act() method of actor.
Although you can use listener to check a state of actor (is it pressed or not) in this way:
//Global instance
ClickListener listenerLeft;
//show() method
...
listenerLeft= new ClickListener();
btnLeft.addListener(listenerLeft);
...
//render() method
...
if(listenerLeft.isPressed())
//perform turning left
...
The second option is to implement ClickListener's touchUp and touchDown methods to change some flag and then check it in render but it would not be doing anything new.
Worth to notice is than both ClickListener and DragListener have touchDragged method that is something about what you want but works only if the mouse/finger is moving when touching actor
listener = new DragListener(){
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer)
{
System.out.println("Left");
}
};
Keeping touching is not an action - no action = nothing to listen

Mouse hover - libgdx

Is there any listener in libgdx that would allow me to detect just mouse hover not pressed just hover. In the button class of scene 2D you have 2 methods isOver and isPressed but they do the same thing ... Anyone else having this problem? Is there another way to detect mouse hover over actor?
There's the ClickListener which can be attached to an Actor and it offers events like the following ones:
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor)
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor)
The enter event basiscally means that the mouse started hovering over the actor, exit means it "left" the area of the actor. It also has a clicked event which you can use to execute some action in the end.

What am I missing in libGDX to get InputEvent on Actor?

I am using libGDX on Android. I set up the Stage and added a CustomActor with a custom draw. It's working properly. However, I am not able to get any logs for the touchDragged method via the InputListener for this actor. Even the code within it does not run.
Here is the required exposure to the code:
public class CustomActor extends Actor {
public CustomActor() {
this.setListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
return true;
}
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
//This log doesn't print up!
Gdx.app.log("CustomActor","touchDragged");
//This code doesn't work either
Vector2 v = CustomActor.this.localToParentCoordinates(new Vector2(x,y));
CustomActor.this.setPosition(v.x, v.y);
}
});
}
}
Can anyone help me out here on what am I missing ?
Add the Stage you are using as InputProcessor to receive the events else the Stage can't forward the event to the actors.
Gdx.input.setInputProcessor(stage);
Should do it.
try using public boolean addListener (EventListener listener) {...} method of Actor. Which version of libgdx are you using? I think actor does not have "setListener" method.
Check your actor bounds. (width & height)

exit event after touchup

I have the following problem in LibGDX. When you move over an Actor you get the enter event from the ClickListener and the exit event when you move out of the bounding box of the Actor. I keep a boolean mouseOver that tells me if the cursor is over an Actor with these events. But when you click on the Actor and after releasing the mouse, an exit event is given. So after releasing the mouse click, it is as if the cursor isn't over the Actor anymore, while it is.
How can I keep a correct state for the boolean mouseOver? In other words: how can I know if the mouse is over my Actor when a TouchUp event has occurred as in the above scenario.
You can override touchUp and set the boolean mouseOver to true there.
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
...
mouseOver = true;
}
The best hack I found so far is to look at the pointer arg in the exit and enter overrides and look if the mouse is clicked. I'm still looking for a cleaner solution though.
addListener(new ClickListener() {
#Override
public void enter(InputEvent event, float x, float y, int pointer,
Actor fromActor) {
mouseOver = pointer == -1 ? true : mouseOver;
super.enter(event, x, y, pointer, fromActor);
}
#Override
public void exit(InputEvent event, float x, float y, int pointer,
Actor toActor) {
mouseOver = pointer == -1 ? false : mouseOver;
super.exit(event, x, y, pointer, toActor);
}
}
Set mouseOver to false only if the Actor toActor of exit is not the actor you added the listener to:
myActor.addListener(new ClickListener() {
...
#Override
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
...
if (toActor != myActor) // and even myActor's children if it is a Group
mouseOver = false;
}
});

Scene2d touchDown isn't fired on Actor

I've created a simple test with a touchDown() event on an image actor. The event works on the stage but not on the actor.
Here is the code:
I set the InputProcessor in the constructor of the parent class (AbstractScreen).
Gdx.input.setInputProcessor(this.stage);
In the sub class:
private TextureAtlas atlas;
private Image boardImg;
public void show(){
super.show();
atlas = new TextureAtlas(Gdx.files.internal("data/packs/mainMenu.atlas"));
AtlasRegion board = atlas.findRegion("board");
boardImg = new Image(board);
boardImg.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
Gdx.app.log("aaaa", "down");
return true;
}
});
stage.addActor(boardImg);
stage.addListener(new InputListener() {
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log("aaaa", "stage down");
return true;
}
});
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
boardImg.setPosition(stage.getWidth()/2 - boardImg.getWidth()/2, stage.getHeight()/4);
}
I'm testing both on the desktop and on the android (same results).
I get the "stage down" event but not the event of the actor. I've tried also without having the stage event.
Do all of these works:
boardImg.setTouchable(Touchable.enabled)
return false in stage listener or clear its listener.
do not call setInputProcessor(this.stage) in parent class instead call in child class.
I think the problem is because the touchDown() method of the stage returns true, and so the event will be "consumed" and won't be propagated to its children, the actors, and so the touchDown() method of boardImg will not be called.
Image doesn't receive touch events because it has no size.
Read documentation carefully:
Note that the actor must specify its bounds in order to receive input
events within those bounds.
Just know that boardImg = new Image(board) won't set width and height of actor for you. So you need to make it manualy. Assume you work with pixel perfect sizes:
boardImg = new Image(board);
boardImg.setWidth(board.getWidth());
boardImg.setHeight(board.getHeight());
That will do the trick. Good luck.

Categories