I have a weird problem in my game. I'm using 2 joysticks, one for shooting/aiming and one for moving my character. For some reason my multitouch method only registers one movement at a time. The second pointer gets registered when I press down, but my ACTION_MOVE only works for the first pointer. This is weird cus this means it does take more then one pointer, but it cant move more then one pointer at the same time. Ive asked this on gamedev.stackexchange and its been active for about a week, gotten a couple of answer but nothing that makes it work 100%. And I've tried for hours on my own.
Code for onTouch-method:
//global variables
private int movePointerId = -1;
private int shootingPointerId = -1;
public void update(MotionEvent event) {
if (event == null && lastEvent == null) {
return;
} else if (event == null && lastEvent != null) {
event = lastEvent;
} else {
lastEvent = event;
}
// grab the pointer id
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
int actionIndex = event.getActionIndex();
int pid = action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int x = (int) event.getX(pid);
int y = (int) event.getY(pid);
String actionString = null;
switch (actionCode)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
actionString = "DOWN";
try{
if(x > 0 && x < steeringxMesh + (joystick.get_joystickBg().getWidth() * 2)
&& y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()){
movingPoint.x = x;
movingPoint.y = y;
movePointerId = pid;
dragging = true;
//checks if Im pressing the joystick used for moving
}
else if(x > shootingxMesh - (joystick.get_joystickBg().getWidth()) && x < panel.getWidth()
&& y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()){
shootingPoint.x = x;
shootingPoint.y = y;
shootingPointerId = pid;
shooting=true;
//checks if Im pressing the joystick used for shooting
}
}catch(Exception e){
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
if( pid == movePointerId ){
movePointerId = -1;
dragging = false;
}
else if( pid == shootingPointerId ){
shootingPointerId = -1;
shooting=false;
}
actionString = "UP";
break;
case MotionEvent.ACTION_MOVE: // this is where my problem is
if( pid == movePointerId ) {
movingPoint.x = x;
movingPoint.y = y;
} else if( pid == shootingPointerId ) {
shootingPoint.x = x;
shootingPoint.y = y;
}
actionString = "MOVE";
break;
}
If I print actionString and pid it shows that when moving, it only checks pid=0, but when i press down ( ACTION_POINTER_DOWN ) I can see that it does register another pid, this is whats really confusing me.
Just to make it more clear, when I press the second pointer down on for example my shooting-stick, it takes the position of where I pressed, even if I'm moving the other joystick at the same time, but it stays there until I let go of the other joystick. Furhter proof that it does register more then 1 touch and more then 1 pid.
Please let me know if you need any further explenation.
I've made a couple of changes to your code, that I believe should solve the problem. Al least it works fine for me ...
//global variables
private int movePointerId = -1;
private int shootingPointerId = -1;
public void update(MotionEvent event) {
if (event == null && lastEvent == null) {
return;
} else if (event == null && lastEvent != null) {
event = lastEvent;
} else {
lastEvent = event;
}
// grab the pointer id
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
int pid = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int fingerid = event.getPointerId(pid);
//int actionIndex = event.getActionIndex();
//int pid = action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int x = (int) event.getX(pid);
int y = (int) event.getY(pid);
String actionString = null;
switch (actionCode)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
actionString = "DOWN";
try{
if(x > 0 && x < steeringxMesh + (joystick.get_joystickBg().getWidth() * 2)
&& y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()){
movingPoint.x = x;
movingPoint.y = y;
//movePointerId = pid;
movePointerId = fingerid;
dragging = true;
//checks if Im pressing the joystick used for moving
}
else if(x > shootingxMesh - (joystick.get_joystickBg().getWidth()) && x < panel.getWidth()
&& y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()){
shootingPoint.x = x;
shootingPoint.y = y;
//shootingPointerId = pid;
shootingPointerId = fingerid;
shooting=true;
//checks if Im pressing the joystick used for shooting
}
}catch(Exception e){
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
if( fingerid == movePointerId ){ //changed this line
movePointerId = -1;
dragging = false;
}
else if( fingerid == shootingPointerId ){ //changed this line
shootingPointerId = -1;
shooting=false;
}
actionString = "UP";
break;
case MotionEvent.ACTION_MOVE: // this is where my problem is
if( fingerid == movePointerId ) { //changed this line
movingPoint.x = x;
movingPoint.y = y;
} else if( fingerid == shootingPointerId ) { //changed this line
shootingPoint.x = x;
shootingPoint.y = y;
}
actionString = "MOVE";
break;
}
The reason for this is that on some devices de pointer id may change when you release one finger. For example, first finger receives pointer id 1, then you press second finger which receives pointer id 2, and if then you release finger 1, pointer id from finger 2 may become 1. It may sound a bit confusing, but you should avoid the issue with this finger id above.
good luck.
Pointers have two different properties available to identify them:
index: it ranges from 0 to one less than the value returned by getPointerCount(). It is only valid during the processing of the current event.
id: this property uniquely identifies the pointer, and is guaranted to stay the same for the pointer during its whole lifetime.
In your code you are not correctly retrieving the index and id info. Basically you are using the index to identify the pointer across several events, something that is completely wrong, as the index may vary, which is what you are experiencing in your app. In short, you should be using the id to identify the pointer.
As I mentioned, they way you are retrieving index and id is wrong. The correct way to retrieve those properties would be:
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pid = event.getPointerId(index);
You can refer here for more info: MotionEvent | Android Developers
private int movePointerId = -1; and private int shootingPointerId = -1;, shouldn't them have different values? i dont know really but once i had it a problem then i changed the values and it worked.
Related
I click 2 fingers on a SurfaceView at a Time, i want get X,Y of 2 Points:
This is my code, but it only get X,Y of 1 Finger.
How can get X,Y of 2 Fingers at a Time?
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int ipoiter = event.getPointerCount();
Toast.makeText(getContext(), String.valueOf(ipoiter), Toast.LENGTH_SHORT).show();
for(int i=0;i<ipoiter;i++){
int x= (int)event.getX(i);
int y = (int)event.getY(i);
Toast.makeText(getContext(), String.valueOf(x), Toast.LENGTH_SHORT).show();
}
}
}
I had try below code but action=0, so can't process.
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
Toast.makeText(getContext(), String.valueOf(action), Toast.LENGTH_SHORT).show();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
int ipoiter = event.getPointerCount();
Toast.makeText(getContext(), String.valueOf(ipoiter), Toast.LENGTH_SHORT).show();
for(int i=0;i<ipoiter;i++){
int x= (int)event.getX(i);
int y = (int)event.getY(i);
Toast.makeText(getContext(), String.valueOf(x), Toast.LENGTH_SHORT).show();
}
}
}
ACTION_POINTER_DOWN is triggered for each newly touched point on screen.
to get the all touch points try something as follows.
You will get the active finger points in mActivePointers
private SparseArray<PointF> mActivePointers = new SparseArray<PointF>();
#Override
public boolean onTouchEvent(MotionEvent event) {
// get pointer index from the event object
int pointerIndex = event.getActionIndex();
// get pointer ID
int pointerId = event.getPointerId(pointerIndex);
// get masked (not specific to a pointer) action
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
// We have a new pointer. Lets add it to the list of pointers
PointF f = new PointF();
f.x = event.getX(pointerIndex);
f.y = event.getY(pointerIndex);
mActivePointers.put(pointerId, f);
break;
}
case MotionEvent.ACTION_MOVE: { // a pointer was moved
for (int size = event.getPointerCount(), i = 0; i < size; i++) {
PointF point = mActivePointers.get(event.getPointerId(i));
if (point != null) {
point.x = event.getX(i);
point.y = event.getY(i);
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL: {
mActivePointers.remove(pointerId);
break;
}
}
invalidate();
return true;
}
Source : Check this
Here is the code for my onTouchEvent(MotionEvent event):
#Override
public boolean onTouchEvent(MotionEvent event){
int action = event.getAction();
int x = Math.round(event.getX());
int y = Math.round(event.getY());
//play is an imageView
int viewLeft = play.getLeft();
int viewRight = play.getRight();
int viewTop = play.getTop();
int viewBottom = play.getBottom();
boolean onPoint = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
//Checks if the touched area falls within the imageView play
if ((x >= viewLeft && x <= viewRight) && (y >= viewTop && y <= viewBottom)) {
onPoint = true;
play.setImageResource(R.drawable.playyellow);
}
case MotionEvent.ACTION_UP:
//if the finger is lifed on the imageView, the intent is called
play.setImageResource(R.drawable.play1);
if (onPoint) {
if ((x >= viewLeft && x <= viewRight) && (y >= viewTop && y <= viewBottom)) {
Intent i = new Intent(this, InGame.class);
startActivity(i);
}
}
}
return true;
}
The problem here is that ACTION_UP is always called, regardless of whether or not I actually lift my finger off the screen. I ran it in debugging mode while placing a breakpoint on case MotionEvent.ACTION_UP, and when I pressed down (and didn't release), it got called. Can someone explain why this happens?
I analyzed your code and I think it is just because you did not insert break in between Action_Down and Action_Up cases.
try the following code.....
switch (action) {
case MotionEvent.ACTION_DOWN:
//Checks if the touched area falls within the imageView play
if ((x >= viewLeft && x <= viewRight) && (y >= viewTop && y <= viewBottom)) {
onPoint = true;
play.setImageResource(R.drawable.playyellow);
}
break;
case MotionEvent.ACTION_UP:
//if the finger is lifed on the imageView, the intent is called
play.setImageResource(R.drawable.play1);
if (onPoint) {
if ((x >= viewLeft && x <= viewRight) && (y >= viewTop && y <= viewBottom)) {
Intent i = new Intent(this, InGame.class);
startActivity(i);
}
}
}
return true;
I hope it will help you...
I'm trying to make a touch event that won't be activated until the finger has moved a few units from the initial position.
So far I've set up my onTouch method like this:
private XYEvents xyEvent = new XYEvents();
public boolean motionTracker(MotionEvent event, int n)
{
int note = n;
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
xyEvent.setInitial(event);
playNote(note);
break;
case MotionEvent.ACTION_MOVE:
byte data1;
byte data2;
//I figured I should input a condition to check if the finger has moved a few units before it should start doing stuff like so:
if (xyEvent.getXThreshold(event))
{
int xMod = xyEvent.eventActions(event)[0];
data1 = (byte) (xMod & 0x7f);
data2 = (byte) ((xMod & 0x7f00) >> 8);
xModulation((int)data1, (int)data2);
}
break;
}
This method is the one I'm having problems with:
private float initialX, initialY;
private int xValue;
boolean getXThreshold(MotionEvent event)
{
float deltaX = event.getX();
float threshold = 10;
float condition = (deltaX - initialX);
if(condition <= threshold || condition >= -threshold )
return false;
else
return true;
}
the getXThreshold method seems to do what it's supposed to in another method that looks like this:
public int[] eventActions(MotionEvent event)
{
int value = xValue;
int xNull = 8192;
if(!getXThreshold(event))
xValue = xNull;
if(getXThreshold(event))
xValue = xHandleMove(event, true);
return value;
}
Any suggestions?
/M
It seems that this argument:
if(condition <= threshold || condition >= -threshold )
return false;
else
return true;
Needed to be flipped around, otherwise it always returned false for some reason.
Now it looks like this and works great.
boolean getXThreshold(MotionEvent event)
{
float deltaX = event.getX();
float threshold = 10;
float condition = (deltaX - initialX);
return condition >= threshold || condition <= -threshold;
}
Have a great week!
/M
I have been studying multitouch on android but i got couldn't understand some of the lines i found.i searched google but couldn't find that understandable resources. i am posting the code.
I understand most of the part except "first two lines of onTouch method", if (event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) and case MotionEvent.ACTION_MOVE:
Please explain them.
Thanks for your help~~
package --- ;
--imports--
#TargetApi(5)
public class MultiTouchTest extends Activity implements OnTouchListener {
StringBuilder builder = new StringBuilder();
TextView textView;
float[] x = new float[10];
float[] y = new float[10];
boolean[] touched = new boolean[10];
int[] id = new int[10];
private void updateTextView() {
builder.setLength(0);
for (int i = 0; i < 10; i++) {
builder.append(touched[i]);
builder.append(", ");
builder.append(id[i]);
builder.append(", ");
builder.append(x[i]);
builder.append(", ");
builder.append(y[i]);
builder.append("\n");
}
textView.setText(builder.toString());
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("Touch and drag(multiple fingers supported!");
textView.setOnTouchListener(this);
setContentView(textView);
for (int i = 0; i < 10; i++) {
id[i] = -1;
}
updateTextView();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int pointerCount = event.getPointerCount();
for (int i = 0; i < 10; i++) {
if (i >= pointerCount) {
touched[i] = false;
id[i] = -1;
continue;
}
if (event.getAction() != MotionEvent.ACTION_MOVE
&& i != pointerIndex) {
continue;
}
int pointerId = event.getPointerId(i);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
touched[i] = true;
id[i] = pointerId;
x[i] = (int) event.getX(i);
y[i] = (int) event.getY(i);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_CANCEL:
touched[i] = false;
id[i] = -1;
x[i] = (int) event.getX(i);
y[i] = (int) event.getY(i);
break;
case MotionEvent.ACTION_MOVE:
touched[i] = true;
id[i] = pointerId;
x[i] = (int) event.getX(i);
y[i] = (int) event.getY(i);
break;
}
}
updateTextView();
return true;
}
}
/*Extract the index of the pointer that touch the sensor
Return the masked action being performed, without pointer index
information.
May be any of the actions: ACTION_DOWN, ACTION_MOVE, ACTION_UP,
ACTION_CANCEL, ACTION_POINTER_DOWN, or ACTION_POINTER_UP.
And return the index associated with pointer actions.*/
**int action = event.getAction() & MotionEvent.ACTION_MASK;**
/* Extract the index of the pointer that left the touch sensor
For ACTION_POINTER_DOWN or ACTION_POINTER_UP as returned by getActionMasked(),
this returns the associated pointer index. The index may be used with
getPointerId(int), getX(int), getY(int), getPressure(int), and getSize(int)
to get information about the pointer that has gone down or up.*/
**int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;**
For more details refer:
Link 1
Link 2
I've a rendered scene that take up to 14 ms to be drawn and displayed.
so the application run without any problem in 60 fps.
but when I start to move my fingers on the device I see that the time increase al lot.
It could take up to 4 ms.
Of course in this case my scene is not displayed in 60 fsp anymore.
is it normal ? May be there is a faster way to detect multitouch ?
my multouch method is this one:
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
int nbPts = ev.getPointerCount();
if (nbPts > _nb_touch_detect) nbPts = _nb_touch_detect;
int pointerIndex = ((ev.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT);
int pointerId = ev.getPointerId(pointerIndex);
int action = (ev.getAction() & MotionEvent.ACTION_MASK);
int pointCnt = ev.getPointerCount();
if (pointCnt <= nbPts)
{
if (pointerIndex <= nbPts - 1)
{
for (int i = 0; i < pointCnt; i++)
{
int id = ev.getPointerId(i);
x_touch[id] = (int)ev.getX(i);
y_touch[id] = (int)ev.getY(i);
}
switch (action)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_MOVE:
isTouch[pointerId] = true;
break;
default:
isTouch[pointerId] = false;
}
}
}
return true;
}