возвращая правильный идентификатор multiTouch

9

Я потратил бесчисленные часы на чтение уроков и просмотр каждого вопроса, касающегося multiTouch, здесь и Stackoverflow. Но я просто не могу понять, как это сделать правильно. Я использую цикл, чтобы получить мой pointerId, я не вижу, чтобы многие люди делали это, но это единственный способ, которым мне удалось заставить его работать.

У меня на экране два джойстика, один для перемещения и один для управления вращением моих спрайтов и углом, который он снимает, как в Monster Shooter. Оба эти прекрасно работают.

Моя проблема в том, что когда я двигаю свой спрайт одновременно со стрельбой, моя touchingPointдля моего движения устанавливается на touchingPointмою стрельбу, так как xи yвыше на touchingPointмоей стрельбе ( 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меняется для моего первого и второго пальца. Я привязываю его к блоку, но пока я держу один палец в этом блоке, он меняет свое значение в зависимости от того, где касается мой второй палец. Он движется в правильном направлении и ничего не дает ошибки, проблема в изменении скорости, почти как если бы он складывал две точки касания.

Макс
источник

Ответы:

4

Я думаю, что это будет работать для вас.

Одна ошибка, которую вы допустили, заключалась в переборе всех указателей для каждого события. Это необходимо только для перемещения событий.

Во-вторых, вам действительно нужно поместить значение индекса в функции getX и getY, но вы получите идентификатор, связанный с этим индексом, для использования в качестве ссылки на ваши игровые объекты. Вы назначаете идентификатор своему джойстику во время события «Вниз», а затем, просматривая индексы указателя, проверяете, связан ли индекс с идентификатором указателя, который вы присвоили джойстику во время события «Вниз». Если это так, проверьте, находится ли он все еще в границах, и либо обновите его, либо отключите.

Я не тестирую этот код, но я знаю, что он работает в концепции, потому что я использую метод в своем собственном коде. Дайте мне знать, если есть какие-то проблемы, которые вы не можете выяснить.

Во-первых, вам нужно добавить следующее в ваш класс джойстика.

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
7

Вы почти все понимаете правильно, но вы должны использовать идентификатор указателя, чтобы запросить X / Y вместо i

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

Из документации MotionEvent:

Порядок, в котором отдельные указатели появляются в событии движения, не определен. Таким образом, индекс указателя указателя может изменяться от одного события к следующему, но идентификатор указателя указателя гарантированно остается постоянным, пока указатель остается активным. Используйте метод getPointerId (int) для получения идентификатора указателя для отслеживания всех последующих событий движения в жесте. Затем для последовательных событий движения используйте метод findPointerIndex (int), чтобы получить индекс указателя для данного идентификатора указателя в этом событии движения.

event.getX/YТребуется идентификатор указателя, нет i, потому что нет гарантии, что они будут в том же порядке.

Кроме того, есть еще одна тонкая, но важная проблема. Обратите внимание, что семейство функций getAction () не принимает параметр. Это немного странно, правда? Для получения X / Y требуется идентификатор указателя, но не выполненное действие? Это намекает на пару важных вещей:

  • вы получаете вызов сенсорного обработчика для каждого действия каждого указателя, а не один вызов на кадр для всех указателей
  • getX / Y пока просматривает трассировку указателя и возвращает последнее значение, тогда как getAction запрашивает только текущее событие

Таким образом, это означает, что вы получаете один вызов к вашему сенсорному обработчику за действие указателя (вниз / перемещение / вверх). Итак, два пальца двигаются = 2 звонка. Противный побочный эффект вашего кода в том, что он применяет действие одного события ко всем указателям ...

Таким образом, вместо циклического обхода трасс, просто получите действие pid / x / y / только для текущего события (для одновременного перемещения вы получите еще один вызов к вашему обработчику чуть позже, как я уже сказал)

Вот мой код для обработки событий:

 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, и сброса их на UP. Поскольку вы не используете жесты, вы можете ограничить свой переключатель () до ВНИЗ, ВВЕРХ, ПЕРЕМЕЩЕНИЕ и СНАРУЖИ.

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
также когда я использую эту строку: int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;eclipse говорит мне, чтобы добавить supressWarning, это нормально? Извините за все вопросы после такого подробного ответа. MotionEvent очень нов для меня, и по какой-то причине не
может
добавил обновленный код, как вы можете видеть, я действительно не знаю, что делать pid, и цикл все еще там, не будет работать без него, так как мне нужно получить i.
Green_qaue
@Max: sendTouchToGameEngine - это просто вызов для добавления этого события в мою очередь игрового движка для обработки во время следующего обновления. Если вы не используете очередь для опроса входного события, вы рискуете связать вызывающую функцию с состоянием игрового движка неожиданными способами, поскольку TouchEvent происходит из потока пользовательского интерфейса и, вероятно, из-за обновления игрового движка. работает в другой теме
АБР
@ Макс: я не знаю о предупреждении, к сожалению. Можете ли вы сказать нам, какой это?
АБР
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;
        }
MichaelHouse
источник
2
Не могли бы вы прокомментировать, почему это не полезно и не заслуживает понижения? Это вежливо.
MichaelHouse
Согласитесь, если вы проголосуете против. Дай ему знать почему. Вы правы по сравнению с прежним методом. Я пробовал около миллиона разных вещей, поэтому я просто забыл его удалить.
Green_qaue
Можете ли вы обновить код в своем вопросе, чтобы отразить это? Я вижу, вы удалили настройку xи y, но вы все еще получаете позицию idпозже.
MichaelHouse
ааа, мне понадобилось время, чтобы понять твой ответ. но как это будет работать? если я использую ту же самую переменную ( x=event.getX(i)), то xмне придется хранить 2 значения, когда pointerCountбольше 1, правильно? И это неправильно.
Green_qaue
1
Из того, что я понимаю event, действительно список событий. Вы получаете доступ к различным событиям, просматривая список, как и вы. Таким образом, чтобы получить доступ к xпозиции для второго события, которое вы используете event.getX(1)(так как мы начинаем с 0). Есть несколько xs, вы используете параметр, чтобы указать, какой из них вы хотите. Вы перебираете все события с помощью своего forцикла, поэтому используйте это число в качестве текущего интересующего вас события. См. Мое предложение по изменению кода.
MichaelHouse
0

У меня когда-то была похожая проблема на Huawei U8150. Мультитач с двумя пальцами на этом устройстве был действительно плохим, с использованием мультитач-тестового приложения (может быть, это «Тестер телефона», но я не уверен), я смог увидеть, что касание вторым пальцем переместило 1-ю точку касания многих пикселей , Если это ваша проблема, то это связано с аппаратным обеспечением, и я не думаю, что вы можете многое сделать для этого :(

Извините за мой плохой английский

Марко Мартинелли
источник
это не проблема
Green_qaue
хорошо, была только мысль
Марко Мартинелли
0

Я полагаю, что ваша проблема заключается в том, что вы предполагаете, что между обновлениями порядок расположения указателей остается неизменным. Скорее всего, это не так.

Представьте себе ситуацию, когда вы касаетесь пальцем А. Указатель будет иметь значение 1, а А будет единственным элементом, о котором вы можете спросить. Если вы добавите второй палец, pointerCount () будет равен 2, A будет с индексом 0, B будет с индексом 1. Если вы поднимите палец A, pointerCount () снова будет равен 1, а B будет с индексом 0 , Если затем снова коснуться пальцем A, A будет с индексом 1, а B будет с индексом 0 .

Вот почему указатель ID предоставляется, чтобы вы могли отслеживать отдельные касания между обновлениями. Таким образом, если первому касанию пальца B назначается идентификатор 12, он всегда будет иметь этот идентификатор, даже когда палец A удален и повторно добавлен.

Поэтому, если ваш код идентифицирует касание рядом с джойстиком съемки, он должен проверить, не выполняется ли уже «стрельба». Если нет, то идентификатор касания должен быть запомнен в некоторой переменной-члене, которая сохраняется до следующего обновления. В последующих обновлениях, если у вас есть «стреляющее» касание, выполняйте итерацию по указателям и ищите указатель с правильным идентификатором, и именно этот указатель вам нужно использовать для отслеживания обновлений, а все остальные можно игнорировать. То же самое касается движения прикосновения. Когда касание отпущено, вы стираете идентификатор, связанный с этим джойстиком, чтобы новое касание могло управлять им.

Даже если касание, начинающееся рядом с одним джойстиком, отклоняется от исходного положения касания, по его идентификатору вы все равно можете правильно определить, каким джойстиком он управляет. И, как приятный побочный эффект, второй случайный палец рядом с конкретным джойстиком не будет иметь никакого эффекта, потому что джойстик будет привязан к первому пальцу, который сработал, до его отпускания.

MrCranky
источник