返回正确的multiTouch id


9

我花了无数小时来阅读教程,并从这里和Stackoverflow上查看与multiTouch相关的每个问题。但是我只是不知道如何正确地做到这一点。我使用循环来获取我的代码pointerId,我看不到有很多人这样做,但这是我设法使其有所工作的唯一方法。

我的屏幕上有两个操纵杆,一个用于移动,另一个用于控制我的精灵旋转和他拍摄的角度,就像在Monster Shooter中一样。这些都很好。

我的问题是,当我在与Im拍摄同时移动我的精灵时,我touchingPoint的动作设置为touchingPoint我拍摄的,因为xand ytouchingPoint拍摄moving-stick的上方较高(在屏幕的左侧,shooting-stick在右侧) ,我的精灵会加速,这会导致我的精灵的速度发生不必要的变化。

这就是我在您的帮助下解决的方法!这适用于可能遇到类似问题的任何人:

    public void update(MotionEvent event) {
    if (event == null && lastEvent == null) {
        return;
    } else if (event == null && lastEvent != null) {
        event = lastEvent;
    } else {
        lastEvent = event;
    }   

        int action = event.getAction();
        int actionCode = action & MotionEvent.ACTION_MASK;
        int pid = action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        int x = (int) event.getX(pid);
        int y = (int) event.getY(pid); 
        int index = event.getActionIndex();
        int id = event.getPointerId(index);
        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;
                            dragging = true;
                            draggingId = id;

                        }
                    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;
                            shooting=true;
                            shootingId=id;
                        }
                    }catch(Exception e){

                    }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE:            
                if(id == draggingId)
                    dragging = false;
                if(id ==  shootingId)
                    shooting = false;
                actionString = "UP";
                break;  
            case MotionEvent.ACTION_MOVE:           
                for(index=0; index<event.getPointerCount(); index++) {
                    id=event.getPointerId(index);
                    int xx = (int) event.getX(index); //pro naming of variable
                    int yy = (int) event.getY(index); 
                    if(dragging && id == draggingId) {
                        if(xx > 0 && xx < (steeringxMesh + joystick.get_joystickBg().getWidth() * 2)
                            && yy > yMesh - (joystick.get_joystickBg().getHeight()) && yy < panel.getHeight()) {
                            movingPoint.x = xx;
                            movingPoint.y = yy;
                        }
                        else
                            dragging = false;
                        }
                    if(shooting && id == shootingId){
                        if(xx > shootingxMesh - (joystick.get_joystickBg().getWidth()) && xx < panel.getWidth()
                            && yy > yMesh - (joystick.get_joystickBg().getHeight()) && yy < panel.getHeight()) {
                            shootingPoint.x = xx;
                            shootingPoint.y = yy;                            
                        }
                        else
                            shooting = false;
                        }
                    }

                    actionString = "MOVE";
                    break;

        }
    Log.d(TAG, "actionsString: " + actionString + ", pid: " + pid + ", x: " + x + ", y: " + y);

如果我绝对不做错事情,就不会发布这么多代码。我根本无法完全了解multiTouching的工作原理。

movingPoint我的第一根和第二根手指基本上都在变化。我将其绑定到一个框,但是只要我在该框中握住一根手指,它就会根据我的第二根手指触摸的位置来更改其值。它朝着正确的方向移动,没有任何错误,问题在于速度变化,几乎就像将两个感测点相加一样。

Answers:


4

我认为这对您有用。

您犯的一个错误是遍历每个事件的所有指针。仅对于移动事件是必需的。

其次,您实际上确实需要将索引值放入getX和getY函数中,但是您会获得与该索引关联的ID,以用作对游戏对象的引用。您在Down事件期间向操纵杆分配一个ID,然后在遍历指针索引时检查索引是否与在Down事件期间分配给操纵杆的指针ID相关联。如果是,请检查它是否仍在范围内,并对其进行更新或禁用。

我没有测试此代码,但是我知道它在概念上是可行的,因为我在自己的代码中使用了该方法。让我知道您是否有任何无法解决的问题。

首先,您必须将以下内容添加到游戏杆类中。

boolean dragging=false;
int draggingId;
boolean shooting=false;
int shootingId;

将您的onTouchEvent更改为此。

public boolean onTouchEvent(MotionEvent event) {


    int index = event.getActionIndex();
    int id = event.getPointerId(index);
    String actionString;

    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            try{
                    if(x > 0 && x < steeringxMesh + joystick.get_joystickBg().getWidth() * 2)
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()
                        && !joystick.dragging) {
                            movingPoint.x = x;
                            movingPoint.y = y;
                            joystick.dragging = true;
                            joystick.draggingId = id;
                    }
                    else if(x > shootingxMesh - (joystick.get_joystickBg().getWidth()) && x < panel.getWidth()
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()
                        && !joystick.shooting) { 
                            shootingPoint.x = x;
                            shootingPoint.y = y;
                            joystick.shooting=true;
                            joystick.shootingId=id;
                    }
              }
              catch(Exception e){

              }

            actionString = "DOWN";
            break;
        case MotionEvent.ACTION_UP:
            if(id == draggingID)
                joystick.dragging = false;
            if(id ==  shootingID)
                joystick.shooting = false;
            actionString = "UP";
            break;  
        case MotionEvent.ACTION_POINTER_DOWN:
            try{
                    if(x > 0 && x < steeringxMesh + joystick.get_joystickBg().getWidth() * 2)
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()
                        && !joystick.dragging) {
                            movingPoint.x = x;
                            movingPoint.y = y;
                            joystick.dragging = true;
                            joystick.draggingId = id;
                    }
                    else if(x > shootingxMesh - (joystick.get_joystickBg().getWidth()) && x < panel.getWidth()
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()
                        && !joystick.shooting) { 
                            shootingPoint.x = x;
                            shootingPoint.y = y;
                            joystick.shooting=true;
                            joystick.shootingId=id;
                    }
              }
              catch(Exception e){

              }

            actionString = "PNTR DOWN";
            break;
        case MotionEvent.ACTION_POINTER_UP:
            if(id == joystick.draggingID)
                joystick.dragging = false;
            if(id ==  joystick.shootingID)
                joystick.shooting = false;
            actionString = "PNTR UP";
            break;
        case MotionEvent.ACTION_CANCEL:
            if(id == joystick.draggingID)
                joystick.dragging = false;
            if(id ==  joystick.shootingID)
                joystick.shooting = false;
            actionString = "CANCEL";
            break;
        case MotionEvent.ACTION_MOVE:
            for(index=0; index<e.getPointerCount(); index++) {
                id=e.getPointerId(index);
                int x = (int) event.getX(index);
                int y = (int) event.getY(index); 
                if(joystick.dragging && id == joystick.draggingId) {
                    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;
                    }
                    else
                        dragging = false;
                    }
                }
                else if(joystick.shooting && id == joystick.shootingId){
                    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;                            
                    }
                    else
                        shooting = false;
                    }
                }
            }
            actionString = "MOVE";
            break;
        }
    }

先生,您是我的英雄。我会将更新的代码添加到我的问题中,以便您可以看到我如何在您的帮助下解决该问题!现在我终于可以完成游戏了:D
Green_qaue 2012年

7

您几乎正确了,但是您应该使用指针ID来请求X / Y而不是 i

    int id = event.getPointerId(i);
    int x = (int) event.getX(id);
    int y = (int) event.getY(id);

从MotionEvent文档中:

未定义单个指针在运动事件中出现的顺序。因此,指针的指针索引可以从一个事件变为下一个事件,但是只要指针保持活动状态,就可以保证指针的指针ID保持不变。使用getPointerId(int)方法获取指针的指针ID,以跨手势中的所有后续运动事件对其进行跟踪。然后,对于连续的运动事件,使用findPointerIndex(int)方法获取该运动事件中给定指针ID的指针索引。

event.getX/Y需要一个指针id,而不是i,因为没有保证,它们的顺序相同。

此外,还有另一个细微但重要的问题。注意getAction()函数系列如何不带参数。有点奇怪吧?获取X / Y需要指针ID,但不需要执行操作吗?这暗示了一些重要的事情:

  • 您会收到针对每个指针的每个操作的触摸处理程序的调用,而不是针对所有指针每帧的单个调用
  • getX / Y查看到目前为止的指针轨迹并返回最新值,而getAction仅查询当前事件

因此,这意味着您可以通过每个指针操作(向下/向上/向上)对触摸处理程序进行一次调用。因此,两个手指移动= 2个呼叫。您的代码的一个讨厌的副作用是它将一个事件的动作应用于所有指针。

因此,无需遍历跟踪,而只需获取当前事件的pid / x / y / action(对于同时移动,您稍后会再一次调用处理程序,就像我说的那样)

这是我处理事件的代码:

 public static boolean sendTouchToGameEngine (MotionEvent event)
 {
  int action = event.getAction();
  int actionCode = action & MotionEvent.ACTION_MASK;
  int pid = action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;

  [...]
  sendTouchToGameEngine(pid, actionCode, (int)event.getX(pid), (int)event.getY(pid));
  [...]

  return true;

}

回到您的代码,您应该可以通过以下方式对其进行简化。该循环已消失,否则,如果您有其他未提及的限制,则可以随时将其添加回去。它通过跟踪触发DOWN时将哪个指针ID用于哪个控件(移动/射击)并将其重置为UP来工作。由于不使用手势,因此可以将switch()限制为DOWN,UP,MOVE和OUTSIDE。

int movePointerId = -1;
int shootingPointerId = -1;

void TouchEventHandler(MotionEvent event) {   
    // grab the pointer id 
    int pid = action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    int x = (int) event.getX(pid);
    int y = (int) event.getY(pid); 
    int action = event.getAction();
    int actionCode = action & MotionEvent.ACTION_MASK;
    int actionIndex = event.getActionIndex();
    String actionString;


    switch (actionCode)
    {
        case MotionEvent.ACTION_DOWN:
        // on DOWN, figure out whether the player used the moving or shooting control, if any.
        // if so, kept track of which pointer was used, because all following call about that
        // finger touches will use the same pointer id. Also record the current point coordinates.
            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;
                    }
                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;
                    }
                }catch(Exception e){

                }
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_OUTSIDE:
        // whether the player lift the finger or moves it out of bounds
        // figure out which pointer that was and reset it. You can add additional
        // processing here as required
           if( pid == movePointerId )
              movePointerId = -1;
           else if( pid == shootingPointerId )
              shootingPointerId = -1;
            actionString = "UP";
            break;  
        case MotionEvent.ACTION_MOVE:
        // when the player move their finger, it is simply a matter of comparing the pid
        // to know which one it is
          if( pid == movePointerId ) {
                        movingPoint.x = x;
                        movingPoint.y = y;
          } else if( pid == shootingPointerId ) {
                        shootingPoint.x = x;
                        shootingPoint.y = y;
          }
                actionString = "MOVE";

    }
}

感谢您提供的出色答案,确实可以解决很多问题。您能否解释一下这行的内容sendTouchToGameEngine(pid, actionCode, (int)event.getX(pid), (int)event.getY(pid));以及何时调用?
Green_qaue 2012年

当我使用这行代码时:int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;eclipse告诉我添加一个supressWarning,这正常吗?如此详细的答案后,对不起所有问题。MotionEvent对我来说是个新手,由于某种原因无法理解逻辑
Green_qaue 2012年

如您所见,添加了更新的代码,我真的不知道该怎么做pid,并且循环仍然存在,没有它就无法工作,因为我需要获取i
Green_qaue 2012年

@Max:sendTouchToGameEngine只是将此事件添加到我的游戏引擎队列中以在下一次更新期间进行处理的调用。如果您不使用队列来轮询输入事件,则可能会导致调用函数以意外的方式与游戏引擎的状态发生混乱,因为TouchEvent来自UI线程,并且可能是游戏引擎的更新运行在不同的线程中
ADB

@Max:不幸的是我不知道警告。你能告诉我们是哪一个吗?
亚行2012年

0

这段代码有点奇怪。我不使用Android,所以也许我在想这不是事实。但是我确实注意到您以两种不同的方式两次获得该职位:

首先,在pointerCount循环开始时,您将获得如下代码:

(int) event.getX(i)

然后,在您的switch语句中,您将得到如下所示:

(int) event.getX(id)

请注意,您已从使用切换i为使用id

我假设它应该是前一种方法。我建议替换所有的实例,(int) event.getX(id)并使用x您在开始时设置的值。同样换(int) event.getY(id)y

尝试换掉这些部分:

int pointerCount = event.getPointerCount(); 
for (int i = 0; i < pointerCount; i++)
{       
    int x = (int) event.getX(i);
    int y = (int) event.getY(i);

然后在交换机内部使用以下命令:

    try{
        if(x > 0 && x < touchingBox &&
              y > touchingBox && y < view.getHeight()){
            movingPoint.x = x;
            movingPoint.y = y;
            dragging = true;
        }
        else if(x > touchingBox && x < view.getWidth() &&
                   y > touchingBox && y < view.getHeight()){
            shootingPoint.x = x;
            shootingPoint.y = y;
            shooting=true;
        }else{
            shooting=false;
            dragging=false;
        }

2
小心评论为什么这没有用并且值得投票吗?这是有礼貌的事情。
MichaelHouse

同意,如果你不赞成的话。让他知道为什么。您从以前的方法是正确的。香港专业教育学院尝试了大约一百万种不同的东西,所以我忘了删除它。
Green_qaue 2012年

您可以更新问题中的代码以反映这一点吗?我看到您删除了x和的设置y,但是id稍后您仍然可以得到该职位。
MichaelHouse

嗯,花了我一段时间才能理解您的答案。但是这将如何工作?如果我使用相同的变量(x=event.getX(i)),那么x只要大于pointerCount1,就必须存储2个值,对吗?那个感觉不对。
Green_qaue 2012年

1
据我了解event,实际上是一个事件列表。您可以像浏览操作一样通过列表来访问不同的事件。因此,x您需要使用第二个事件的位置event.getX(1)(因为我们从0开始)。有多个x,您正在使用参数来告诉您要使用哪个。您正在循环浏览所有事件for,因此请将该数字用作您感兴趣的当前事件。请参阅我的代码更改建议。
MichaelHouse

0

我曾经在华为U8150上遇到过类似的问题。使用多点触控测试应用程序(可能是“ Phone Tester”,但我不确定),该设备上的两指多点触控确实很糟糕,我能够看到第二根手指的触摸移动了许多像素的第一个触摸点。如果那是您的问题,而不是硬件方面的问题,并且我认为您不能为此做很多事情:(

对不起,我的英语不好


那不是问题
Green_qaue 2012年

好的,只是个想法
Marco Martinelli,2012年

0

我相信您的问题是,您假设在两次更新之间,指针的排列顺序保持不变。事实并非如此。

想象一下您用手指A触摸的情况。pointerCount()为1,并且A是您唯一可以询问的元素。如果添加第二根手指,pointerCount()将为2,A将位于索引0,B将位于索引1。如果您举起手指A,则指针Count()将再次为1,而B将位于索引0。。如果然后再次用手指A触摸,则A将在索引1处,而B将在索引0处

这就是提供指针ID的原因,以便您可以跟踪更新之间的各个触摸。因此,如果手指B的第一次触摸被分配了ID 12,即使手指A被卸下并重新添加,它也将始终具有该ID。

因此,如果您的代码识别出拍摄操纵杆附近的触摸,则必须检查是否已经在进行“射击”触摸。如果不是,那么应该在一些成员变量中记住射击触摸的ID,该成员变量会保留到下一次更新。在后续更新中,如果正在进行“拍摄”操作,请遍历指针并查找具有正确ID的指针,这是您用来跟踪更新的指针,所有其他指针都可以忽略。运动触感相同。释放触摸时,清除与该操纵杆关联的ID,以便新的触摸可以控制它。

即使在一个操纵杆附近开始的触摸偏离原始触摸位置也很远,但通过其ID,您仍然可以正确地识别它正在控制的操纵杆。而且,作为一个不错的副作用,靠近特定操纵杆的第二根流浪手指不会产生任何效果,因为操纵杆将绑定到触发它的第一根手指,直到释放它为止。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.