Android multi-touch interference - android

I'm currently developing an air-hockey simulation for android. For the multiplayer mode I'm tracking two touch events on the screen, which works well as long as the touch points don't get to close.
When the two fingers get to close, android only recognizes one touch event, in the middle of both points.
To make it even worse, android sometimes messes up the IDs after the collision.
I already thought about estimating the next touch points ans assigning IDs manually, does anybody know a better way, or knows about somebody who already fixed this problem programmatically?
NOTE: I'm testing on a Samsung Galaxy S 3

Not necessarily a logical fix to the issue, nevertheless a possible solution to the application:
If I'm not completely mistaken, air-hockey games shouldn't allow opponents to intrude on each others game field. If we assume a thick border cross the center of the screen (in portrait mode), then I wouldn't be allowed to do anything beyond that border, hence there is no point in tracking my finger after it reaches the border line.
Encapsulating your tracked touch events into valid physical locations as described might just help you in ignoring invalid points (given that the physical locations doesn't intersect, that is).
You might also have to keep track of direction of the touch vector: if the vector is stretching from the center of the screen towards "your end" it might be the opponents intruding finger or your own returning finger. In neither case should they affect the hockey puck (perhaps).

It may depend on the device you are using, but I'm using the code below in a Huawei X5 and It never mess up fingers, even if they touch each other or it I twist them over the screen.
private static PointF touchScreenStartPtArr[] = new PointF[10];
private static PointF touchScreenStopPtArr[] = new PointF[10];
private static PointF touchScreenCurrPtArr[] = new PointF[10];
OnTouchListener onTouchListenerMulti = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int fingerId = event.getPointerId(pointerIndex);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
touchScreenStartPtArr[fingerId].x = event.getX(pointerIndex);
touchScreenStartPtArr[fingerId].y = event.getY(pointerIndex);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
touchScreenStopPtArr[fingerId].x = event.getX(pointerIndex);
touchScreenStopPtArr[fingerId].y = event.getX(pointerIndex);
break;
case MotionEvent.ACTION_MOVE:
int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
touchScreenCurrPtArr[fingerId].x = event.getX(i);
touchScreenCurrPtArr[fingerId].y = event.getY(i);
}
break;
}
return true;
}
};
Note that I'm using fingerId and not pointerId to identify the correct finger, as pointer id may change when one finger is released.
Hope it works for you.

Here's the way I see it.
The touchscreen hardware gives you a resolution below which two touches are the same as one. This is something you cannot change.
Now the question is, what to do when two touches merge? (This is something that can be tested for programmatically one would think; e.g. if 2 touch pts -> 1 touch pt. AND prev touch pt 1 is close enough to prev touch pt 2...). In your case, I would move both pucks along the merged touch gesture until they separate, then return individual control.
Of course, I see several problems with this, like which touch controls which puck after the split? Maybe one person lifted their finger during the merge.
You could have both players lose control of their puck if a merge occurs. This could simulate the shock to the wrist as your hand bashes into your opponent's :)
I also like #dbm idea.
Hope this helps. Probably didn't :)

Related

Detect finger switch on touch

I have a bitmap that I can drag around on my app, but there is a small bug, if let's say someone clicks with one finger on the bitmap and starts moving it around, and then while moving it around clicks with another finger, the bitmap "teleports" over there. How can I detect if the user switched fingers so bitmaps wont teleport? this is my Action_MOVE code:
case MotionEvent.ACTION_MOVE:
if (dragging) {
CurrentMobEntry.getKey().x = (int) x
- CurrentMobEntry.getValue().getNormalbit()
.getWidth() / 2;
CurrentMobEntry.getKey().y = (int) y
- CurrentMobEntry.getValue().getNormalbit()
.getHeight() / 2;
CurrentMobEntry.getValue().getDestroyedP().x=(int)x
+CurrentMobEntry.getValue().getNormalbit()
.getWidth() / 2;
CurrentMobEntry.getValue().getDestroyedP().y = (int)y
+ CurrentMobEntry.getValue().getNormalbit()
.getHeight() / 2;
}
break;
From the documentation:
Some devices can report multiple movement traces at the same time. Multi-touch screens emit one movement trace for each finger. The individual fingers or other objects that generate movement traces are referred to as pointers. Motion events contain information about all of the pointers that are currently active even if some of them have not moved since the last event was delivered.
The number of pointers only ever changes by one as individual pointers go up and down, except when the gesture is canceled.
So what you want to do is to allow only for one pointer (finger) to operate in any given time. I can't seem to find a simple solution to this so we are going to use a workaround:
Each pointer gets a unique, constant ID. On any given touch event, you will check to see if that pointer initiated the event and then if true consume it, else ignore.
For a full example of this check the Android Developer's blog.

Dragging objects using ACTION_MOVE

I am trying to make all my drawn Sprites dragable for a little game. It should be able to touch anywhere and the sprites should move the same distance, the finger moves.
With the following method they will move on an ACTION_MOVE event, but only very slow, a shorter distance and sometimes they dont:
addToX/Y only adds the gap to the coordinates of the sprites
#Override
public boolean onTouchEvent(MotionEvent evt){
switch(evt.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(getHistorySize() > 0){
for(int i = 1, n = evt.getHistorySize(); i < n; i++){
int calcX = (int) getHistoricalX(i) - (int) getHistoricalX(i-1);
int calcY = (int) getHistoricalY(i) - (int) getHistoricalY(i-1);
for(Sprite sprite : spriteList) {
sprite.addToX(calcX);
sprite.addToY(calcY);
}
}
}
return true;
}
Any ideas on this?
Assuming your Sprite class is an (potentially-indirect) extension of android.view.View, then you can use setOnDragListener() to define an onDrag() override for them. Then you can use startDrag(...) on them to begin the drag. This is typically triggered by a long-press gesture on the view to be dragged, but in your case you can trigger it from within onTouchEvent() in ACTION_MOVE once (or even ACTION_DOWN). See here for more details on these methods.
Also, with respect to the code you posted, one issue with it that probably explains why it doesn't always work is that you are only using the historical points (which may or may not have accumulated on any particular call to onTouchEvent()). Whether or not getHistorySize() is greater than 0, you should still also use evt.getX() and evt.getY() on each call to onTouchEvent(). But of course, if you use the drag listener approach I suggested instead, you won't need to worry about this.
Update per comment
If you want to move all of the sprites at once, you can put the sprites into a full-screen FrameLayout and attach a GestureDetector that uses a GestureDetector.SimpleOnGestureListener to capture onScroll() callbacks and then calls scrollTo() on the FrameLayout. When the parent FrameLayout scrolls, all of its children sprites will appear to move together.

For what "finger" getActionIndex and getActionMasked refer to?

I'm trying to figure out all the multi - touch event handling in android for couple of hours and I just don't get it.
If I have multi touch event MotionEvent stores all fingers data right?
Say I touch the screen with 3 fingers simultaneously or move 2 fingers on the screen simultaneously when asking getActionIndex or ( getActionMasked ) I get one result but as I move or ( touch the screen with 3 fingers ) so how do I know for which finger getActionIndex or ( getActionMasked ) refers to?
Thank u in advance
ID is used to identify and track pointers (let just say pointer is your finger). While index for a particular touch gesture might change (you might lift the finger etc) but id remains the same (thus used for tracking gestures).
int pointerCount = m.getPointerCount();
for (int i = 0; i < pointerCount; i++)
{
int x = (int) m.getX(i);
int y = (int) m.getY(i);
int id = m.getPointerId(i);
if(id==0) {
...First touch....
}
........
I am also new to android development. Was reading about multi touch yesterday. Hope this helps.

Android (AndEngine): How to obtain touch input events outside onSceneTouchEvent method?

So, here's my problem: in my game I made a countdown at the start of the game, during which the user can't press the screen, so I wait until the countdown is over to enable the OnSceneTouchListener. As soon as it's enabled, the user will be able to do some stuff moving the finger (not important what).
The problem is that if the user starts moving the finger while there's the countdown, as soon as it's over the onSceneTouchEvent method starts getting inputs, but it skips the ACTION_DOWN event since the user is already moving the finger.
To prevent this I could of course use a boolean inside the onSceneTouchEvent method, but this way it would always check that for every single input. It doesn't influence much the performance, but I'd rather find another way if it's possible.
So I was thinking, is there a way in the AndEngine to obtain the touch input event (with the information of the coordinate pressed, that's what I really need), so that I could "force" the ACTION_DOWN event by simple doing what I should do with it in the method that makes the countdown?
Thanks in advance!
Here is an example, how you can get touch coordinates.
public boolean onTouchEvent(MotionEvent event) {
int myEventAction = event.getAction();
float X = event.getX();
float Y = event.getY();
switch (myEventAction) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE: {
mySprite.setPosition(X, Y);
break;}
case MotionEvent.ACTION_UP:
break;
}
return true;
}
Source of this example

Android will treat fast single touch as move?

I write a very simple android application, that I can draw something on the pad. Touch the screen with a finger, you will see a green ball, move your finger, you will see a red line.
But I found a very strange thing: If I touch the screen with two fingers one by one very fast, it will draw a line between them! (Imaging you are pressing two keys jkjkjkjkjkjjkjkjkjkjkjkj on the keyboard)
The key code is pretty simple:
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
multiTouch = false;
id = event.getPointerId(0);
PointF p = getPoint(event, 0);
path = new Path();
path.moveTo(p.x, p.y);
paths.add(path);
points.add(copy(p));
break;
case MotionEvent.ACTION_POINTER_DOWN:
multiTouch = true;
for (int i = 0; i < event.getPointerCount(); i++) {
int tId = event.getPointerId(i);
if (tId != id) {
points.add(getPoint(event, i));
}
}
break;
case MotionEvent.ACTION_MOVE:
if (!multiTouch) {
p = getPoint(event, 0);
path.lineTo(p.x, p.y);
}
break;
}
invalidate();
return true;
}
The full source is here: https://github.com/freewind/TouchTest/blob/master/src/com/example/MyImageView.java
And it's a working demo: https://github.com/freewind/TouchTest
Or you can just download the signed apk on your android device, and test it yourself: https://github.com/freewind/TouchTest/blob/master/TouchTest.apk?raw=true
You can see in my code, I have checked if it's multi touch and disabled drawing on that case.
My android version is 4.0, and my code target is 2.3.3
There is a picture on my android pad:
You can see there are some lines but it should not be, there should be a green ball on the left of the red line instead.
I'm not sure why android treat fast single touch as moving, I considered 3 reasons:
My code has something wrong
Android sdk has something wrong
My android pad has something wrong, e.g. missing a ACTION_DOWN event
How to find out the real reason?
UPDATE
One of my friend used his android mobile(android 2.1) to test this app and found there is no red line, another used android 2.3.5 and found there are red lines.
Please review my code, I have checked multi-touch by ACTION_POINTER_DOWN, and will do nothing on ACTION_MOVE if there are more than 1 points. So the id of point is not needed. (Actually, in my first version of this code, I used id but have the same issue).
And I don't think this is an expected behavior, because it made the development of touching programs hard. I found this issue because in my another application(user can drag/zoom/rotate an image by fingers), the image sometimes "jump" on screen.
I even tried a popular game (Fruit Ninja) on my android pad and iTouch, and found android version has the issue but iTouch doesn't.
Now I'm sure there is something wrong (missing an ACTION_UP event when the first finger ups), but I still don't know what causes it. My android pad? Or Android sdk?
That is way it works for multitouch. When you press fast android handle it as gesture, and you will have 2 pressed pointers. To avoid it try to handle action_up, or use action_pointer_down instead
you can check the id of the touch , so that you will handle only the first touch alone.
alternatively , you can monitor all touches and handle them together .

Categories

Resources