I am building a simple game with LIBGDX, and I have come across this irritating problem.
I have a MenuScreen.java class, which looks like this:
Here's the full class on pastebin
The important part is the imageButton's inputListener:
button.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
MineFinder.Log.debug("xpos: " + x + ", ypos: " + y);
game.setScreen(new GameScreen(game));
}
});
My problem is, that if this class is present (and GameScreen gets called from this) for some reason if I click to the ImageButton's position in GameScreen, it still handles the input. This is really irritating, because if a player clicks on that part of the screen the game gets reseted.
If i remove references to my MenuScreen.java file, and skip it alltogether the problem isn't present, so I am pretty sure that the problem is that the InputListener doesn't get "deleted"
Any ideas how to fix this?
Thanks!
The problem is that in your resize method. This line:
Gdx.input.setInputProcessor(stage);
sets the global input processor to be the stage. The input handling code doesn't really care about what is being rendered on the screen.
I think the easiest fix is to set the input processor in show and clear it in hide. Something like this:
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
}
#Override
public void hide() {
Gdx.input.setInputProcessor(null);
}
Alternatively, you could have your callbacks check to see if the button/stage/screen they are in is active, or unregister/re-register the callbacks in show/hide, depending on how you are using your Screens.
Related
I am designing my own clickListener class. When i touch down on any actor registered with my clicklistener, i would like to pause all the actions on the actor, and only call it back when touch up is trigger. I tried with the following codes but it give me a total hang everytime i triggered touchUp.
public class MyClickListener extends ClickListener {
public Actor actor;
Array<Action> cachedActions;
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
super.touchUp(event, x, y, pointer, button);
actor = event.getListenerActor();
actor.addAction(btnScaleBackActions());
for(Action a: cachedActions)
{
a.reset();
a.setTarget(actor);
a.setActor(actor);
actor.addAction(a); //this line give me a total hang
}
}
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if(pointer==0) // avoid further trigger on other buttons while holding the selected actor
{
actor = event.getListenerActor();
actor.setScale(0.9f);
cachedActions = actor.getActions();
actor.clearActions();
if(autoSetSound)AudioManager.playSound(AudioManager.CLICK_IN);
return super.touchDown(event, x, y, pointer, button);
}
else
{
return false;
}
}
public static Action btnScaleBackActions(){
float time = 0.1f;
return sequence(
scaleTo(1,1,time ),
scaleTo(0.95f,0.95f,time/4),
scaleTo(1,1,time/4)
);
}
}
It shows no error but only white screen. Any help?
The problem is this line:
cachedActions = actor.getActions();
You are getting a reference to the Actor's own list of actions instead of making a copy. Incidentally, on the next line (actor.clearActions();) you are clearing the list so cachedActions is empty.
Later on touch up, the actor (and cachedActions) now have the action you added (btnScaleBackActions()). You are cycling through an array, adding the same object to it forever. The iterator can never finish because you are always adding more, so it is an infinite loop.
You need to create your own list for cached actions and copy the items over.
private final Array<Action> cachedActions = new Array<Action>();
Then copy the actions, not the reference in touch down:
cachedActions.addAll(actor.getActions());
actor.clearActions();
And make sure to clear cachedActions at the end of touchUp.
I am making a game in Android Studio using LibGDX, and I am attempting to add a pause button to the top corner of the main game screen. My game has been pausing fine thus far, but I have only been pausing when a key was pressed. Now I want to add a button, and I got the button to render in the top right corner, but when I press it it only works once. The first time I press it, my game pauses fine. But every time I try and pause it after that, it doesn't work. I have used extensive log statements and debugging and have found out that after pressing it once, the Listener doesn't even detect the button being pressed at all, so I am lead to believe that is where my issue is.
This is how I create my button in my HUD class, which is the class that prints the score onscreen at all times:
TextureAtlas buttonAtlas = new TextureAtlas(Gdx.files.internal("Buttons.pack"));
Skin skin = new Skin();
skin.addRegions(buttonAtlas);
ImageButton.ImageButtonStyle style = new ImageButton.ImageButtonStyle();
style.imageUp = skin.getDrawable("PauseButton");
style.imageDown = skin.getDrawable("PauseButton");
style.imageChecked = skin.getDrawable("PauseButton");
button = new ImageButton(style);
I then added a Listener to check if the button is clicked:
button.addListener(new ClickListener()
{
#Override
public void clicked(InputEvent event, float x, float y)
{
Gdx.app.log("","Button was pressed");
pauseGame();
}
});
The method pauseGame() that is called after the Listener detects the button being clicked:
public void pauseGame()
{
Gdx.app.log("","Game should be pausing");
screen.pauseGame();
}
screen is a class called PlayScreen that is the main game screen for my game, and its pauseGame() method works perfectly. The game before you press the pause button is as follows(You can see the pause button in the top right corner, please excuse the graphics, they are simply placeholders until I make and add my own graphics):
Game before pause
In my main PlayScreen class, this is my pauseGame method:
public void pauseGame()
{
gamePaused = true;
if(music.isPlaying())
{
musicType = type.NORMAL;
music.pause();
}
else if(barrageMusic.isPlaying())
{
musicType = type.BARRAGE;
barrageMusic.pause();
}
createPauseWindow();
}
And then in my render method, I call another method called update. In this method, I update my viewport, my HUD, and create new enemies. This is the only method that stops being called when gamePaused is true. The rest of my rendering and other necessary updates still take place. I have been trying to fix this problem for a long time but no matter how many different ways I rewrite the code to pause the game or make different listeners, the pause button only works one time and then never again.
Have you ever tried with touchDown method?
It might works.
button.addListener(new ClickListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
pause();
event.handle();
return true;
}
});
And try to change to a global variable base, a variable that is checked inside your render method and if it is true call the pause method. That to avoid call complex actions like pause from a listener, something like this:
button.addListener(new ClickListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
pause = true;
event.handle();
return true;
}
});
...
public void render(){
if (pause == true){
pauseGame();
}
...
}
It simplifies your listener and let you find easily your error.
So i created a button:
tile = new ImageButton(up,null,checked);
tile.setPosition(x, 800);
tile.setSize(118,200);
tile.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
return true;
}
});
Right now it changes from the up texture to the checked image every time the button is touched. But what I want is that the texture changes to checked ,only the first time the button is touched and becomes "untouchable" after. Is there any way to do this? EventListeners don't have any feature like these.
ImageButton has a method setTouchable(Touchable touchable) inherited from Actor. "Determines how touch events are distributed to this actor. Default is Touchable.enabled."
Touchable hase three enum constants:
childrenOnly: No touch input events will be received by the actor, but
children will still receive events.
disabled: No touch input events will be received by the actor or any
children.
enabled: All touch input events will be received by the actor and any
children.
On the first touch call setTouchable(Touchable.disabled) on that particular ImageButton
Use a boolean field at class level e.g.
class SomeClass{
private boolean isTileTouched = false;
}
And use that in your ClickListener
tile.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if(isTileTouched) return false;
isTileTouched = true;
return true;
}
});
If you have more then one button you might consider using a Map<String,Boolean> for the tileId and the value.
where should I implement dialog.hide to hide my dialog when the user touches the screen in libgdx, I mean outside the borders of the dialog.
I'm looking for something similar to the following in Android SDK.
dialog.setCanceledOnTouchOutside(true);
Stage has the size of your screen, so, you can add input listener on stage
stage.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
//you action here
stage.removeActor(dialog);
return true;
}
});
dialog must be the field of your class, of course
So i have an actor which is a sprite, set on a screenviewport stage. What i want to do is be able to touch the actor, then touch a spot on the screen it will move that fluently. Currently when i touch the actor it just jumps seemingly to random spots. Here is some of the code in my actor class,
public MyActor(){
setBounds(sprite.getX(),sprite.getY(),
sprite.getWidth(),sprite.getHeight());
setTouchable(Touchable.enabled);
addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
MoveByAction mba = new MoveByAction();
mba.setAmount(x,y);
MyActor.this.addAction(mba);
return true;
}
});
}
#Override
protected void positionChanged() {
sprite.setPosition(getX(),getY());
super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha) {
sprite.draw(batch);
}
#Override
public void act(float delta){
super.act(delta);
}
Few things to ask here. First of all, your touch is only on touching your Actor. That's going to be exactly where the actor is. You need to implement at your Scene level some basic state machine to know "on first tap, this actor is selected", then it has to be in a "select where this actor goes" state, and finally when you select a position it has to send that XY to the selected actor to do the move.
Now, as Tenfour04 mentioned, you have to figure out the conversion of the screen's XY of the touch point into the game world's coordinates. Tenfour04 made a good comment about using the projection method on the viewport camera to achieve this. Once you do that, you can send the coordinates to your actor to do the previously mentioned stuff.
To achieve the movement, I'd use the Action framework, like below:
actor.addAction(moveTo(x, y, 0.4f, Interpolation.circle));
This page shows you all the nice actions available for Scene2d: https://github.com/libgdx/libgdx/wiki/Scene2d#actions
Hope that's what you needed. :)