I would like to use some of the more complex touch screen gestures that you can't access from
Gdx.input
I saw that to do this i must create a Gesture listener so i created the class GestureHandler and copied the code from the wiki. My gesture handler looks like this:
public class GestureHandler implements GestureListener {
#Override
public boolean touchDown(float x, float y, int pointer, int button) {
return false;
}
#Override
public boolean tap(float x, float y, int count, int button) {
return false;
}
#Override
public boolean longPress(float x, float y) {
return false;
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
return false;
}
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
return false;
}
#Override
public boolean zoom(float initialDistance, float distance) {
return false;
}
#Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
return false;
}
}
My question is now that i have set up the gesture listener how can i use it. How can i get the info from these methods? Thank you for any help!
From the wiki:
A GestureDetector is an InputProcessor in disguise. To listen for
gestures, one has to implement the GestureListener interface and
pass it to the constructor of the GestureDetector. The detector is
then set as an InputProcessor, either on an InputMultiplexer or as
the main InputProcessor
I admit that is rather dense. But a bit farther down on the wiki you'll see:
Gdx.input.setInputProcessor(new GestureDetector(new MyGestureListener()));
To rephrase the above in hopefully less dense English: Your GestureHandler instance is passed to a Libgdx GestureDetector instance. That object will accumulate "raw" inputs and convert them into higher-level "gestures". To get the raw inputs, it needs to be installed where the raw inputs will be delivered to it. The most basic way to install it via Gdx.input.setInputProcessor, but you could also install it via an InputMultiplexer (but that's not worth getting into here).
Related
I am trying to set up proper multitouch support in my android game for some time. For input handling I use my InputHandler(implementing InputProcessor). I use something like this
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector3 touchPos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
screen.renderer.cam.unproject(touchPos);
if (left.isTouchDown((int) touchPos.x, (int) touchPos.y)) {
player.setMovingLeft(true);
player.leftPressed();
}
and this
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
Vector3 touchPos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
screen.renderer.cam.unproject(touchPos);
if (left.isTouchUp((int) touchPos.x, (int) touchPos.y)) {
player.setMovingLeft(false);
player.leftReleased();
}
I have set up 3 buttons like that, but I have no idea why it doesn't work. All the answers I've found aren't very helpful when using InputProcessor.
I know that it has something to do with touch pointers and iterating input, but that's where my knowledge ends, unfortunately.
The code "when touched" and "when released" is working for sure, I'm using it the same way with keyboard input. The only problem is multitouch.
I encoutered similar problem once,trying to set up 3 buttons on screen. Those buttons can be pressed separately (then there is no problem, you always use just one finger and one pointer - therefore pointer ID can be ignored), but they should also react properly, if any two of them are pressed, or player presses all at once.
You have to make use of pointer ID, given by InputProcessor. Pointer ID is an int from 0 to 9 range (for 10 fingers).
I did it that way:
first declared three variables for three pointers:
int forwardPointer, leftPointer, rightPointer;
then in TouchDown method i checked, which finger pressed which button (was it first, second or third press?), and stored this information in my variables for future use.
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (game.forwardButton.contains(screenX, screenY)) {
game.forwardButton.isPressed=true;
forwardPointer=pointer;
}
if (game.leftButton.contains(screenX, screenY)) {
game.leftButton.isPressed=true;
leftPointer=pointer;
}
if (game.rightButton.contains(screenX, screenY)) {
game.rightButton.isPressed=true;
rightPointer=pointer;
}}
and then, in TouchUp method i checked, which finger was lifted up (by comparing pointer ID from TouchUp method with stored pointer variable value), and according to this, i "relased" proper button.
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if (pointer==forwardPointer) {
game.forwardButton.isPressed=false;
}
if (pointer==leftPointer) {
game.leftButton.isPressed=false;
}
if (pointer==rightPointer) {
game.rightButton.isPressed=false;
}}
This worked for me. No need to use Gdx.input.getX(), Gdx.input.getY(), as
those values are already given by touchDown and touchUp method (screenX and screenY).
You're mixing the libGDX input polling interfaces with the libGDX input event API. Specifically, these calls:
Gdx.input.getX(), Gdx.input.getY()
are equivalent to:
Gdx.input.getX(0), Gdx.input.getY(0)
see the getX documentation. So you're always reading the x/y coordinates of the 0th touch index, independent of which touch is active.
The event API is passing in the x, y and pointerID to the callback, so you just need to do something like:
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector3 touchPos = new Vector3(screenX, screenY, 0);
screen.renderer.cam.unproject(touchPos);
if (left.isTouchDown((int) touchPos.x, (int) touchPos.y)) {
player.setMovingLeft(true);
player.leftPressed();
}
You should use the "pointer" parameter to distinguish fingers during multi-touch.
Since pointer value passed in event methods is a unique id of the touching finger, you can use a hashmap to store and manipulate all of the touches with that unique key.
Create a hashmap.
public static HashMap<Integer, Vector2> touchlist;
public Constructor(){
touchlist=new HashMap<>();
}
Add as new if touch is not present.
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if(!touchlist.containsKey(pointer))touchlist.put(pointer, new Vector2(screenX, screenY));
return false;
}
Remove if finger is up.
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
touchlist.remove(pointer);
return false;
}
Update the corresponding touch event if finger is dragged
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
Vector2 touch=touchlist.get(pointer);
if(touch!=null)touch.set(screenX, screenY);
return false;
}
Key is the pointer and value is a vector that will store the touch coordinates.
And to use them anywhere you want just iterate over the map
for(Integer key:touchlist.keySet()){
Vector2 touchpos=touchlist.get(key);
//do your manipulation here
if(isvalid(touchpos)) //use if valid skip if not etc..
}
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.
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.
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)
how to perform single touch on isActionMove() because when i move finger on sprites it takes multipal touch events and update scores twice thrice
mHardware[active] = new Sprite(pX, pY, java, this.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float X, float Y) {
if (pSceneTouchEvent.isActionMove()) {
score++;
}
}
};
i cant use isActionDown because its a game like fruit ninja in which i need to move finger across screen
now problem is score is sometimes increasse by 2 sometimes by 3 because when i move finger on sprite application notices several short movements in place of one
you should use
private static boolean isFirstMove;
public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float X, float Y) {
if (pSceneTouchEvent.isActionDown()) {
isFirstMove = true;
}
if (pSceneTouchEvent.isActionMove()) {
if(isFirstMove) {
score++;
isFirstMove = false;
}
}
if (pSceneTouchEvent.isActionUp()) {
isFirstMove = false;
}
});
If you found some what above answer correct for you then I have some suggestion in this.
You have to implement both scene and area touch event for your game scene.
SceneTouch event method contains two events isActionDown() and isActionUp() like in the following code.
public void onSceneTouchEvent(...){
if(pSceneTouchEvent.isActionDown()){
isFirstTouch=true;
return true;
} else if(pSceneTouchEvent.isActionUp()){
isFirstTouch=false;
return true;
}
return false;
}
Area Touch method contains only single event isActionMove() like in the following code.
public void onAreaTouch(...){
if(pSceneTouchEvent.isActionMove()){
if(isFirstMove) {
score++;
isFirstMove = false;
}
return true;
}
return false;
}
You have to follow above strategy because some time desire event not occur for your sprite so you don't get your desire result.
solution given by julien dumortier is working perfectly
static boolean isFirstMove=true;
public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float X, float Y) {
if (pSceneTouchEvent.isActionMove()) {
if(isFirstMove) {
score++;
isFirstMove = false;
}
}
});