Scene2d touchDown isn't fired on Actor - java

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.

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

Libgdx InputListener "exit()" is not being called with TextButton

I want to use the exit() method in the InputListener to see wheter the cursor is inside the button or not.
Here is the explanation in the libGDX docs.
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor)
Called any time the mouse cursor or a finger touch is moved out of an actor.
But when I put my cursor on the button and then move it outside the button, the method is not called. I am testing it by a System.out.println("exited"); and I get nothing in the console.
EDIT:
LibGDX Version: Latest Stable Nightlies
InputListener implementation:
//This button class is a custom class to make button creation easier. This is the constructor.
public Button(Vector2 position, String packLocation, String text, Stage stage, BitmapFont font, Color color) {
//Removed buttonStyle creation etc. to shorten the code.
button = new TextButton(text, buttonStyle);
button.setPosition(position.x, position.y);
stage.addActor(button);
Gdx.input.setInputProcessor(stage);
pressed = false;
button.addListener(new ClickListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
pressed = true;
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
pressed = false;
}
#Override
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
System.out.println("exited");
}
});
}
EDIT:
Hovering over the button also does not change the texture of the button as I set it to like so:
buttonStyle.over = skin.getDrawable("over");
But clicking does.
After searching for a couple hours I finally found what was missing. stage.act(); had to be called in the render method. This both gave functionality to the texture change when we hover over the button and also the enter/exit methods in the InputListener.

libGDX Mouse over on UI element fires same event from underlying Actor

I'm making a simple RTS game. I've created separate Stages for map and UI, and used scene2D Table class for side panel. The problem is, when i hover my side panel, if there is an actor (building) under panel at the moment, it fires its mouseover event. Click events work properly.
Here is my building class input listeners:
public class Building extends Actor {
addListener(new InputListener(){
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
System.out.println("Click");
return true;
}
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor){
((Building)event.getTarget()).hover = true;
}
Here is my panel class listeners
public class SidePanel extends Table {
panelBg = new Image(skin,"side-panel");
addListener(new InputListener(){
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
System.out.println("Click");
return true;
}
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor){
System.out.println("Enter");
}
});
addActor(panelBg);
}
}
Then side panel is added to UI class stage:
public class UI {
public UI(){
stage = new Stage();
sidePanel = new SidePanel();
stage.addActor(sidePanel);
Gdx.input.setInputProcessor(stage);
}
And finally i added UI to main class:
#Override
public void create () {
ui = new UI();
CP =new InputMultiplexer();
CP.addProcessor(ui.stage);
CP.addProcessor(gameStage);
Gdx.input.setInputProcessor(CP);
}
Not sure where the problem is, because click event works fine;
One quick solution would be to have your Building class check if the mouse was also over the SidePanel. For example, you could change your Building listener's enter method to something like this:
if (mouseIsOverSidePanel)
((Building)event.getTarget()).hover = true;
Your SidePanel listener's enter method can be something like this:
mouseIsOverSidePanel = true;
And your leave method in your SidePanel can be:
mouseIsOverSidePanel = false;
This is mostly pseudo code so it won't work without declaring the variables (obviously) but it should give you a basic idea of how to fix your problem.

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)

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

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?

Categories