Android MultiTouch only working for one finger - java

I am developing a two player Pong game and wish to control the bats, each with one finger. However, my problem is that it only responds to one finger being on the screen at a time. The entire game is ran in one view. First, I check if a pointer is "active" before checking if it occured on the top or bottom of the screen before moving the corresponding bat. It works normally, except for that only one touch registers. My code for the method is below:
// variables
int _touchY = (int) event.getY();
boolean isTopSide = true;
// test if motionEvent was on the top or bottom of the screen
if (SCREEN_HEIGHT / 2 - _touchY < 0) {
isTopSide = false;
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
_activePointer = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if(_activePointer != INVALID_POINTER_ID) {
if (isTopSide) { // top bat
_topBat.moveBat(event);
} else { // bottom bat
_botBat.moveBat(event);
}
}
if(_newPointer != INVALID_POINTER_ID){
if (isTopSide) { // top bat
_topBat.moveBat(event);
} else { // bottom bat
_botBat.moveBat(event);
}
}
break;
case MotionEvent.ACTION_UP:
if(_activePointer != INVALID_POINTER_ID){
_activePointer = INVALID_POINTER_ID;
}
break;
case MotionEvent.ACTION_CANCEL:
_activePointer = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
int pointerIndex = event.getActionIndex();
int pointerId = event.getPointerId(pointerIndex);
if(pointerId == _activePointer){
_activePointer = INVALID_POINTER_ID;
}
case MotionEvent.ACTION_POINTER_DOWN:
int newPointerIndex = event.getActionIndex();
_newPointer = event.getPointerId(newPointerIndex);
break;
}
Relevant global code
// touched
private static final int INVALID_POINTER_ID = -1;
private int _activePointer = INVALID_POINTER_ID;
private int _newPointer = INVALID_POINTER_ID;

The main problem why you're only seeing values for a single pointer is because event.getY() without any parameter gives you the position for the first pointer index, even if the pointer that actually moved was the second pointer.
The MotionEvent class contains the coordinates for all the active pointers.
If you want to get the second pointer's position, you'll want to call event.getY(1).
You'll probably want to loop through all the active points, getting the total count via event.getPointerCount(), and then send the events accordingly.

Related

Handle Touch Event on both segments of the screen simultaneously

So I have a touch event and it handles ACTION_DOWN and ACTION_UP one by one. For example if I click on the left half of the screen ACTION_DOWN works but not ACTION_UP and same for the right side of the screen. i want to handle both of them simultaneously. If I click on left side and right side at the same time both the event should perform. Can anyone please help me on this.
My code is
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//if user press the screen
if (event.getX() < screenX / 2f) {
flight.isGoingUp = true;
}
break;
case MotionEvent.ACTION_UP:
flight.isGoingUp = false;
if (event.getX() > screenX / 2f) {
flight.toShoot++;
}
break;
}
return true;
}
Edit 1
The screen is split into two half. The left side of the screen is used for flight.isGoingUp = true; and when we release the left side or tap the right helg of the screen flight.isGoingUp = false; and if (event.getX() > screenX / 2f) { flight.toShoot++; } I want ot do such a code that both of them are handled at the same time.
here is the docs to handle multi touch gestures.
https://developer.android.com/training/gestures/multi#java
Here is a very simple example to handle multitouch based on google docs.
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getActionMasked();
int index = event.getActionIndex();
System.out.println("The finger # " + index + " is " + getAction(action));
return true;
}
public static String getAction(int action) {
switch (action) {
case MotionEvent.ACTION_DOWN: return "Down";
case MotionEvent.ACTION_MOVE: return "Move";
case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
case MotionEvent.ACTION_UP: return "Up";
case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
case MotionEvent.ACTION_OUTSIDE: return "Outside";
case MotionEvent.ACTION_CANCEL: return "Cancel";
default: return "Unknown";
}
}

How to implement "Two finger swipe" gesture in android?

I have researched a lot but I couldn't find even a blog that tell about MultiTouch events. All are giving tutorials on single swipe. I wish to apply fling and 2 finger swipe both in same activity.
I got the solution myself after trying and failing a lot
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: " + event.getAction());
//with the getPointerCount() i'm able to get the count of fingures
if(event.getPointerCount()>1){
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_POINTER_DOWN:
// This happens when you touch the screen with two fingers
mode = SWIPE;
// event.getY(1) is for the second finger
p1StartY = event.getY(0);
p2StartY = event.getY(1);
break;
case MotionEvent.ACTION_POINTER_UP:
// This happens when you release the second finger
mode = NONE;
float p1Diff = p1StartY - p1StopY;
float p2Diff = p2StartY - p2StopY;
//this is to make sure that fingers go in same direction and
// swipe have certain length to consider it a swipe
if(Math.abs(p1Diff) > DOUBLE_SWIPE_THRESHOLD
&& Math.abs(p2Diff) >DOUBLE_SWIPE_THRESHOLD &&
( (p1Diff>0 && p2Diff>0) || (p1Diff < 0 && p2Diff<0) ))
{
if(p1StartY > p1StopY)
{
// Swipe up
doubleSwipeUp();
}
else
{
//Swipe down
doubleSwipeDown();
}
}
this.mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(mode == SWIPE)
{
p1StopY = event.getY(0);
p2StopY = event.getY(1);
}
break;
}
}
else if(event.getPointerCount() == 1){
//this is single swipe, I have implemented onFling() here
this.gestureObject.onTouchEvent(event);
}
return false;
}

Java Snake Bug: It happens when change direction quickly

In my Snake code, if the snake is moving LEFT currently, and I then press either UP or DOWN, and then very quickly press RIGHT, the game is over. I knew the reason why it happened, but I don't know how to fix it. Maybe I should announce a new variable for last direction and then compare it with the new direction to decide if the snake should turn to the new direction? I don't know how to fix it. Can somebody help me to solve the problem ? :-)
Here is the relevant code.
//Judge the keyboard input
public void changeDirection(Direction newDirection) {
if (snakeDirection.compatibleWith(newDirection)) {
snakeDirection = newDirection;
}
Direction.java
public enum Direction {
UP(0),
RIGHT(1),
DOWN(2),
LEFT(3);
private final int directionCode;
Direction(int directionCode) {
this.directionCode = directionCode;
}
private int directionCode() {
return directionCode;
}
//
public boolean compatibleWith(Direction direction) {
return (this.directionCode() + direction.directionCode()) % 2 == 1;
}
}
GameController.java
public class GameController implements Runnable, KeyListener {
private final Grid grid;
private final GameView gameView;
boolean running;
public GameController(Grid grid, GameView gameView) {
this.grid = grid;
this.gameView = gameView;
this.running = true;
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_UP:
grid.changeDirection(Direction.UP);
break;
case KeyEvent.VK_DOWN:
grid.changeDirection(Direction.DOWN);
break;
case KeyEvent.VK_LEFT:
grid.changeDirection(Direction.LEFT);
break;
case KeyEvent.VK_RIGHT:
grid.changeDirection(Direction.RIGHT);
break;
case KeyEvent.VK_ENTER:
if (running == false) {
grid.init();
running = true;
new Thread(this).start();
}
break;
case KeyEvent.VK_SPACE:
if (running) {
running = false;
} else {
running = true;
new Thread(this).start();
}
break;
default:
break;
}
}
// the Thread
public void run() {
while (running) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
break;
}
// your code here
if (grid.nextRound()) {
gameView.draw();
} else {
gameView.showGameOverMessage();
break;
}
}
running = false;
}
You really need a new variable. Your snakeDirection is the direction you want to use in the next time step and comparing it with newDirection only helps when it has been used. You need a lastUsedDirection set after a step was done.
Some comments
You can replace directionCode by ordinal().
You should start only a single thread and let it skip the action when running=false. Use the constructor allowing to start it with deamon=true, so that you can terminate the game without it terminating first. Don't let it terminate. You current code risks having multiple threads running when you press SPACE multiple times fast enough.
Be careful with you variable used in multiple threads. As long as one thread only reads the variable and the other one only writes it, it's enough to use volatile. You probably don't even do this and then it can happen that one thread never sees the writes made by another one (this is no theory; this really happens, maybe not in your case).
Just check the x and y positon of the head and the "neck".
What I mean is:
neck
|
| xxxx
| x <--- The snake
HNxx
|
|
head
If the position is the same in one axis, you should not turn.
Here's an actual example (it's C++, but Java has similar syntax, so this shouldn't be a problem).
void Snake::setDirection(char dir) {
switch (direction) {
case Snake::NORTH:
if (dir == Snake::SOUTH) return;
if (body.size() > 1 && body[0]->y == body[1]->y) return;
break;
case Snake::SOUTH:
if (dir == Snake::NORTH) return;
if (body.size() > 1 && body[0]->y == body[1]->y) return;
break;
case Snake::EAST:
if (dir == Snake::WEST) return;
if (body.size() > 1 && body[0]->x == body[1]->x) return;
break;
case Snake::WEST:
if (dir == Snake::EAST) return;
if (body.size() > 1 && body[0]->x == body[1]->x) return;
break;
}
direction = dir;
}
body is a std::vector that holds all the snake segments and direction is a Snake class variable.

Android 2 finger touch triggering 1 finger touch as well as 2 finger touch

I have an issue in my app where I need to perform a different action based on if one fingers or two fingers are pressed down.
Currently I have the follow code:
#Override
public boolean onTouchEvent(MotionEvent event){
int touchX = (int)event.getX();
int touchY = (int)event.getY();
int action = event.getActionMasked();
//Multitouch
if(event.getPointerCount() > 1 && action == MotionEvent.ACTION_POINTER_DOWN){
System.out.println("2 Finger press");
return true;
}
else if(action == MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_POINTER_DOWN){
if(event.getPointerCount() == 1) {
System.out.println("single finger down");
invalidate();
return true;
}
}
return false;
}
The issue I am having is when I press with 2 fingers, the multitouch part registers, and after that so does the single press.
I did some googling around on ways to fix that and that would be why I am checking on the single touch conditional that action != MotionEvent.ACTION_POINTER_DOWN which I thought would fix the issue. That didn't fix the issue hence I decided to check that event.getPointerCount() == 1, but unfortunately that still causes both lines to print when I press with 2 fingers.
To summarize I need to only call the 2 finger part when 2 fingers are pressed down and not both.
refer to the documentation on "Handling Multi-Touch Gestures". it details exactly what you're looking for.
in essence, each active pointer is assigned an ID when it makes contact with the screen. your solution will should take into account which IDs come into play and are removed by observing various action states.
something a little bit along these lines (but beware compatibility calls for methods like getActionIndex()):
UPDATED CODE SAMPLE
public class MotionActivity extends AppCompatActivity {
private int primaryPointerId = -1;
private int secondaryPointerId = -1;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new FrameLayout(this));
}
#Override
public boolean onTouchEvent(MotionEvent event){
int action = event.getActionMasked();
switch(action) {
case MotionEvent.ACTION_DOWN:
primaryPointerId = event.getPointerId(0);
Log.d(getClass().getName(), "Primary pointer ID == [" + primaryPointerId + "]");
break;
case MotionEvent.ACTION_POINTER_DOWN:
secondaryPointerId = event.getPointerId(event.getActionIndex());
Log.d(getClass().getName(), "Secondary pointer ID == [" + secondaryPointerId + "]");
break;
case MotionEvent.ACTION_MOVE:
if(primaryPointerId > -1 && secondaryPointerId > -1) {
Log.d(getClass().getName(), "Two-point touch...");
} else {
Log.d(getClass().getName(), "One-point touch...");
}
break;
case MotionEvent.ACTION_POINTER_UP:
if(event.getPointerId(event.getActionIndex()) == primaryPointerId) {
primaryPointerId = secondaryPointerId;
}
secondaryPointerId = -1;
break;
case MotionEvent.ACTION_UP:
primaryPointerId = -1;
break;
}
return true;
}
}

How to handle multi touch?

I'm making a platfrom game for android and I could use some help with the touch events.
This is my code:
public boolean onTouch(MotionEvent e, int scaledX, int scaledY) {
for (int i = 0; i < object.size(); i++) {
tempObject = object.get(i);
if (tempObject.getId() == ObjectId.Player) {
if (e.getAction() == MotionEvent.ACTION_MOVE) {
if (moveLeft.contains(scaledX, scaledY)) {
tempObject.setMovingLeft(true);
tempObject.setMovingRight(false);
}
if (moveLeftExit.contains(scaledX, scaledY)
&& !moveLeft.contains(scaledX, scaledY)) {
tempObject.setMovingLeft(false);
}
if (moveRight.contains(scaledX, scaledY)) {
tempObject.setMovingRight(true);
tempObject.setMovingLeft(false);
}
if (moveRightExit.contains(scaledX, scaledY)
&& !moveRight.contains(scaledX, scaledY)) {
tempObject.setMovingRight(false);
}
}
if (e.getAction() == MotionEvent.ACTION_UP
|| e.getAction() == MotionEvent.ACTION_OUTSIDE) {
if (moveLeft.contains(scaledX, scaledY)) {
tempObject.setMovingLeft(false);
}
if (moveRight.contains(scaledX, scaledY)) {
tempObject.setMovingRight(false);
}
}
if (e.getAction() == MotionEvent.ACTION_DOWN) {
if (jump.contains(scaledX, scaledY)) {
if(tempObject.getVelY() ==0)
tempObject.setVelY(-15);
}
}
}
}
return true;
}
Everything works great while I use one finger, if I touch the moveRight rectangle the character moves right and when I move my finger away he stopes as expected. The problem is that if I touch a button while touching some other button it wont react to it.
So I guess my question is, how can I modify my code so it will react to multi touch?
Thanks!! :)
Here is a simple code.
public boolean onTouch(View v, MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
int pointerIndex = MotionEventCompat.getActionIndex(event);
int x = (int)MotionEventCompat.getX(event,pointerIndex);
int y = (int)MotionEventCompat.getY(event,pointerIndex);
switch(action)
{
case MotionEvent.ACTION_DOWN:
//
// First finger was touched. Set "pointerIndex" to some value (lets say 0 or -1)
// Save "pointerIndex" corresponding to this touched object.
//
break;
case MotionEvent.ACTION_POINTER_DOWN:
//
// More finger touched when first finger is already touched.
// Save "pointerIndex" corresponding to this touched object.
//
break;
case MotionEvent.ACTION_MOVE:
//
// Finger with "pointerIndex" was moved.
//
break;
case MotionEvent.ACTION_UP:
//
// The first touched finger went off.
//
break;
case MotionEvent.ACTION_POINTER_UP:
//
// Finger with "pointerIndex" went off (other than first finger)
//
break;
}
return true;
}
Good Luck. :)

Categories

Resources