I'm using an actors with a stage as buttons. I can detect when touchDown/touchUp events occur just fine over the actor, but when the user taps on the actor and then proceeds to drag their finger off the actor, the touchUp event never fires. I tried to use the exit event instead, but it never fires. In my program the touchUp/touchDown events determine movement and also button color which depend on whether the button is being pressed or not. So I' left with a permanently "depressed" button until it's clicked down/up again.
Example of the code I'm using:
stage.addListener(new InputListener() {
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
Actor actor = stage.hit(x, y, true);
if (actor != null){
System.out.println("touchDown: " + actor.getName().toString());
}
return true;
}
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
Actor actor = stage.hit(x, y, true);
if (actor != null){
System.out.println("touchUp: " + actor.getName().toString());
}
}
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor){
System.out.println("exit");
}
});
If you change
stage.addListener(new InputListener() {});
to
stage.addListener(new ClickListener() {});
it will recognize the TouchUp call. And it still will be able to handle the TouchDown, and Exit calls.
I had the same problem. I fixed it by creating boolean isDown variable as a field of my GameScreen class. Whenever touchDown occures on my background Image I make isDown variable true and when touchUp occures - isDown = false. That way touchUp will always occur. Then still in my GameScreen in render method i check if isDown is true, if it is, I check if the touch intersects with my actor:
if (isDown) {
if (pointIntersection(myActor, Gdx.input.getX(), Gdx.input.getY())) {
// do something
}
} else {
// reverse the effect of what you did when isDown was true
}
where pointIntersection method is:
public static boolean pointIntersection(Image img, float x, float y) {
y = Gdx.graphics.getHeight() - y;
if (img.x <= x && img.y <= y && img.x + img.width >= x && img.y + img.height >= y)
return true;
return false;
}
It's the only workaround I found to that. Still, it's not very pretty but works for me.
Related
I'm trying to close a dialog on background touch but it always goes in the else condition
stage.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if(stage.hit(x,y,true).equals(bg)) {
System.out.println("in th if");
dialog.addAction(rotateTo(90, .30f, Interpolation.smooth2));
dialog.hide();
}
else {
System.out.println("int the else");
}
return true;
}
});
I think this will work, but didn't test.
Dialog is already set up to receive all touchDown input while it's visible, even if the touch is outside its bounds, so simply give it a listener that hides it if the touch is outside its bounds:
dialog.addListener(new InputListener() {
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
if (x < 0 || x > dialog.getWidth() || y < 0 || y > dialog.getHeight()){
dialog.hide();
}
return true;
}
});
This assumes dialog is final or a member field so you can access it from the listener.
I think the reason your code doesn't work is that stage.hit(...) will always return the dialog regardless of coordinates since Dialogs are set up to absorb all input.
I'm making a game where swiping and justTouched do different things. My problem is that when I swipe on the screen obviously the touchDown() method is also being triggered.
Im extending GestureAdapter:
#Override
public boolean touchDown(float x, float y, int pointer, int button) {
//move when screen is touched
if(life == 1 && overlaps == false) {
timeState = 0;
velocity.y = -120;
velocity.x = 100;
}
return super.touchDown(x, y, pointer, button);
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
if(velocityX > 10)
//do something
return super.fling(velocityX, velocityY, button);
}
You should not use just touched in this case because when player touched first time you cant know will it be just touch or swipe.
You must use justtouched with touch up method like this
when player justtouched save touched coordinates.
Check coordinates when player touched up.
If distance between this 2 points less than 50 pixels (or what limit you want to set) then you can assume its justtouched otherwise its swipe and you can do calculations about swiping.
public boolean touchDragged(int screenX, int screenY, int pointer) {
if(check) {
sprite.setPosition(screenX - sprite.getWidth() / 2, Gdx.graphics.getHeight() - screenY - sprite.getHeight() / 2);
rect.setPosition(screenX - sprite.getWidth() / 2, Gdx.graphics.getHeight() - screenY - sprite.getHeight() / 2);
}
return false;
}
this is my method in the my custom input processor class i use Input multiplexer in my main because i have 2 classes . Simultaneous drag won't move the sprite i can only move one sprite at a time.
My intention is to drag 2 sprites at the same time.
Thanks for your help and sorry for my bad English.
I don't know if this is the best approach, but a kinda-solution could be something like this:
1. Add a constant that let you know how many object you will allow to move at the same time:
private static final int MAX_TOUCHES = 4;
2. Add a collection with a fixed size, with this you'll manage all sprite that are currently being possible moving:
private final Array<Sprite> sprites = new Array<Sprite>(MAX_TOUCHES);
3. Now, in your class where you are handling touches, implement the touchDown(), touchDragged() and touchUp():
/**
* In the touchDown, add the sprite being touched
**/
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
// Just allow 4 sprites to move at same time
if(pointer >= MAX_TOUCHES) return true;
// Get the sprite at this current position...
Sprite sprite = getSpriteAtThisPosition(screenX, screenY);
// If sprite found, add to list with current pointer, else, do nothing
if(sprite != null) {
sprites.set(pointer, sprite);
}
return true;
}
getSpriteAtThisPosition() is just a method that return the first current sprite in that position, could return null.
/**
* In the touchDragged, move this sprite
**/
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
// Just allow 4 sprites to move at same time
if(pointer >= MAX_TOUCHES) return false;
// Get the sprite with the current pointer
Sprite sprite = sprites.get(pointer);
// if sprite is null, do nothing
if(sprite == null) return false;
// else, move sprite to new position
sprite.setPosition(screenX - sprite.getWidth() / 2,
Gdx.graphics.getHeight() - screenY -
sprite.getHeight() / 2);
return false;
}
/**
* In the touchUp, remove this sprite from the list
**/
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
// Just allow 4 sprites to move at same time
if(pointer >= MAX_TOUCHES) return true;
// remove sprite at pointer position
sprites.set(pointer, null);
return true;
}
The InputMultiplexer is not for dealing with two classes of object to move but to handle two input processors (or more) that you want to have - for example when you have more than one Stage and want to interact with player with each of them.
What you should do here would be to remember what pointer is attached to the touched object and then move it according to the pointer movement. The pointer is just id of for example finger enumerated from 0. So when you will touch screen with first finger it is 0 pointer, second one is 1 - BUT! If you will leave first finger keeping second the second is still 1 so it is perfect for this usage.
To handle remembering pointer id you also need to implement touchDown listener method
The code example would be like:
HashMap<Integer, Sprite> ids = new HashMap<Integer, Sprite>();
...
public boolean touchDown (int x, int y, int pointer, int button)
{
Sprite sprite = getSprite(x, y); //getSprite should iterate over all sprites in your game checking if x/y is inside one of them - you need to implement this one
ids.put(pointer, sprite);
return true;
}
public boolean touchUp (int x, int y, int pointer, int button)
{
ids.remove(pointer); //removing from hashmap
return false;
}
public boolean touchDragged (int x, int y, int pointer)
{
Sprite sprite = ids.get(pointer);
if(sprite != null)
{
sprite.setPosition... //and so on
}
return false;
}
For getting more information about multitouch handling you can check out this tutorial.
I want to drag an Actor around with the Mouse in LibGDX.
My code:
// overrides in the ClickListener
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
touching = true;
prevTouchPosition = new Vector2(x, y);
return super.touchDown(event, x, y, pointer, button);
}
#Override
public void touchDragged(InputEvent event, float x, float y,
int pointer) {
dragging = true;
lastTouchPosition = new Vector2(x, y);
super.touchDragged(event, x, y, pointer);
}
#Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
touching = false;
super.touchUp(event, x, y, pointer, button);
}
// the update method
#Override
public void act(float delta) {
super.act(delta);
if(dragging){
Vector2 diff = new Vector2(lastTouchPosition).sub(prevTouchPosition);
translate(diff.x, diff.y);
prevTouchPosition = new Vector2(lastTouchPosition);
dragging = false;
}
}
The more I move the Actor around the worse it gets.
The idea is to keep the last two Mouse positions and use the difference between them to update the position of the Actor.
Anything incremental like this is going to suffer from roundoff errors. Moreover, I think you don't need to update prevTouchPosition every frame; in the absence of roundoff errors, it should not be changing. Note that your code only works if the object is not rotated or scaled.
A more robust drag algorithm works as follows:
upon touch down, touch point L in object-local coordinates:
G := L
upon touch move, touch point L in object-local coordinates:
setPosition(toGlobal(L) - toGlobal(G) + getPosition())
Note that we never update G during the drag operation; it is used to remember the point on the object where it was "grabbed", and we continuously align the object so that this point is under the current touch. Here, toGlobal(L) - toGlobal(G) gives you the "delta" vector in global coordinates, which we add to the current position.
Note that I'm assuming there's no object hierarchy, so the current position is itself in global coordinates. Otherwise you'll need to take the parent's transform into account as well.
here is my code now:
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getAction();
switch(action)
{
case MotionEvent.ACTION_DOWN:
// Do click work here ...
Y -= 10;
Yopen = 1;
break;
case MotionEvent.ACTION_UP:
// Do release work here ...
Yopen = 0;
break;
}
return super.onTouchEvent(event);
}
But I want to make only three different areas to perform different code.
Can some one help me, some good tutorial on the internet.
Add this code to your onTouchEvent to figure out where the user touched and respond appropriately:
//Grab the current touch coordinates
float x = event.getX();
float y = event.getY();
//If you only want something to happen then the user touches down...
if (event.getAction() != MotionEvent.ACTION_UP) return true;
//If the user pressed in the following area, run it's associated method
if (isXYInRect(x, y, new Rect(x1, y1, x2, y2)))
{
//Do whatever you want for your defined area
}
//or, if the user pressed in the following area, run it's associated method
else if (isXYInRect(x, y, new Rect(x1, y1, x2, y2)))
{
//Do whatever you want for your defined area
}
Here's the isXYinRect method:
//A helper method to determine if a coordinate is within a rectangle
private boolean isXYInRect(float x, float y, Rect rect)
{
//If it is within the bounds...
if (x > rect.left &&
x < rect.right &&
y > rect.top &&
y < rect.bottom)
{
//Then it's a hit
return true;
}
//Otherwise, it's a miss
return false;
}