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..
}
Related
So I am currently busy with a kind of pong game for android. All i have currently is the paddle and the ball. The controls for the player (the paddle) is as followed:
If player touches on left side of screen, paddle goes left
If player touches on right side of screen, paddle goes right
Actually the above 2 points work fine, but what bothers me, is that when im holding right with my right finger, and after that release it and really fast holding left with my left finger, the paddle only executes one time "going left", and then stops, while I am still holding left. (and vice versa)
I discovered this only happens if I use two fingers. If im holding down right with my finger, and try to go really fast to press the other side, it doesnt stop and actually keeps going left.
But it is important to use two fingers, since that is the way how the game should be played.
This all may be explained unclear, so you can ask in the comments more specific questions.
Player.java: http://pastebin.com/pdFZJTRB
MyInputProcessor.java: http://pastebin.com/XPzi8JPB
I think your problem resides in the fact that you have the touchDown boolean being shared between the left and right sides. If you press the left side fast enough, the touchDown for the left finger gets set to true before the touchDown for the right finger is set to false, thus making them all false. Try something like this instead:
package com.nahrot.teleportball;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.math.Vector2;
public class MyInputProcessor implements InputProcessor
{
public Vector2 leftTouchPos = new Vector2();
public Vector2 rightTouchPos = new Vector2();
public boolean touchLeft = false;
public boolean touchRight = false;
#Override
public boolean keyDown(int keycode)
{
return false;
}
#Override
public boolean keyUp(int keycode)
{
return false;
}
#Override
public boolean keyTyped(char character)
{
return false;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button)
{
if(sideTouched(screenX, true))
{
touchLeft = true;
leftTouchPos.set(screenX, screenY);
}
if(sideTouched(screenX, false))
{
touchRight = true;
rightTouchPos.set(screenX, screenY);
}
return false;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button)
{
if(sideTouched(screenX, true))
{
touchLeft = false;
}
if(sideTouched(screenX, false))
{
touchRight = false;
}
return false;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer)
{
return false;
}
#Override
public boolean mouseMoved(int screenX, int screenY)
{
return false;
}
#Override
public boolean scrolled(int amount)
{
return false;
}
private boolean sideTouched(int x, boolean checkLeft)
{
if(checkLeft)
{
if(x <= Gdx.graphics.getWidth() / 2)
return true;
}
else
{
if(x > Gdx.graphics.getWidth() /2)
return true;
}
return false;
}
}
Here, I separated the booleans and vectors into left side and right side, and made a convenience function for checking whether a touchDown or touchUp is on the right or left side of the screen, and adjusted the corresponding booleans and vectors accordingly. I assume the vector was needed to check which side was pressed anyway, so I suspect you could work it so that all you need is the two booleans for the sides touched. This code is untested, by the way, but you should get the idea if it doesn't work.
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 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;
}
});
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).
I am adding bodies with fixtures to a box2d world in libgdx.
I want to detect if the user has touched (clicked) an object.
How do I do this? thanks
You should use libgdx Stage in order to detect touch events on Actors (what you are referring to them as Objects here). The best practice is to map a box2d body to a stage actor which makes it quite simple to do things like that.
To detect touch:
Implement touchDown method of the InputProcessor interface such that:
You have to transform your screen coordinates to stage coordinates using stage.toStageCoordiantes(...) method.
Use the transformed coordinates to detect hit on the Actor (Object) on stage using stage.hit(x, y).
stage.hit(x, y) will return you the actor if a hit is detected.
Hope that helps.
User touches the body only if he touches some of Fixture's contained into this body. This mean, you can check every Fixture of Body using testPoit() method:
public class Player {
private Body _body;
public boolean isPointOnPlayer(float x, float y){
for(Fixture fixture : _body.getFixtureList())
if(fixture.testPoint(x, y)) return true;
return false;
}
}
Next, you need to create InputAdapter like this:
public class PlayerControl extends InputAdapter {
private final Camera _camera;
private final Player _player;
private final Vector3 _touchPosition;
public PlayerControl(Camera camera, Player player) {
_camera = camera;
_player = player;
// create buffer vector to make garbage collector happy
_touchPosition = new Vector3();
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
// don't forget to unproject screen coordinates to game world
_camera.unproject(_touchPosition.set(screenX, screenY, 0F));
if (_player.isPointOnPlayer(_touchPosition.x, _touchPosition.y)) {
// touch on the player body. Do some stuff like player jumping
_player.jump();
return true;
} else
return super.touchDown(screenX, screenY, pointer, button);
}
}
And the last one - setup this processor to listen user input:
public class MyGame extends ApplicationAdapter {
#Override
public void create () {
// prepare player and game camera
Gdx.input.setInputProcessor(new PlayerControl(cam, player));
}
Read more about touch handling here