повернення правильного ідентифікатора 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);

Не хотів би розмістити цей великий код, якщо я не втратив абсолютної втрати того, що я роблю неправильно. Я просто не можу добре зрозуміти, як працює мультитач.

в основному movingPointзмінюється як для мого першого, так і для другого пальців. Я прив'язую його до коробки, але, поки я тримаю один палець у цій коробці, вона змінює свою величину залежно від того, де торкається другий палець. Він рухається в правильному напрямку, і нічого не дає помилки, проблема полягає в зміні швидкості, майже так, як вона додає два торкаються точки.

Відповіді:


4

Я думаю, це допоможе тобі.

Одна з помилок, яку ви зробили, - це повторення всіх покажчиків для кожної події. Це потрібно лише для переїздів.

По-друге, вам дійсно потрібно ввести значення індексу у функції getX та getY, але ви отримаєте ідентифікатор, пов'язаний з цим індексом, щоб використовуватись як посилання на ваші ігрові об’єкти. Ви присвоюєте ідентифікатор джойстику під час події Down, а потім, перебираючи покажчики покажчиків, перевірте, чи індекс пов’язаний з ідентифікатором вказівника, який ви присвоїли джойстику під час події Down. Якщо він є, перевірте, чи він ще знаходиться в межах, або оновіть його або вимкніть.

Я не тестую цей код, але знаю, що він працює в концепції, тому що я використовую метод у власному коді. Повідомте мене, чи є з цим якісь проблеми, які ви не можете зрозуміти.

По-перше, вам доведеться додати наступне до класу джойстика.

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, та перезавантаження їх вгору. Оскільки ви не використовуєте жестів, ви можете обмежити свій перемикач () вниз, вгору, переміщення та назовні.

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 підказує мені додавати супрессПопередження, це нормально? Вибачте за всі запитання після такої детальної відповіді. MotionEvent для мене дуже нова, і я не можу чомусь зрозуміти логіку
Green_qaue

додано оновлений код, як ви бачите, я дійсно не знаю, що з цим робити pid, і цикл все ще є, і без нього працювати не буду, оскільки мені потрібно дістати i.
Green_qaue

@Max: sendTouchToGameEngine - це просто заклик додати цю подію до черги мого ігрового двигуна, яка буде оброблена під час наступного оновлення. Якщо ви не використовуєте чергу для опитування події введення, ви ризикуєте несподіваними способами переплутати свою функцію виклику зі станом ігрового двигуна, оскільки TouchEvent походить з потоку інтерфейсу користувача і, імовірно, оновлення вашого ігрового двигуна. працює в іншій темі
ADB

@Max: На жаль, я не знаю про попередження. Чи можете ви сказати нам, хто це?
ADB

0

З цим кодом відбувається невелика дивність. Я не використовую Android, тому, можливо, я думаю, що це щось не так. Однак я помітив, що ви отримуєте посаду двічі, двома різними способами:

Спочатку ви отримуєте це так на початку вашого pointerCountциклу:

(int) event.getX(i)

Тоді, всередині заяви перемикання, ви отримуєте це так:

(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

Чи можете ви оновити код у своєму запитанні, щоб це відобразити? Я бачу, що ви видалили налаштування 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 у мене колись була схожа проблема. Мультитач з двома пальцями на цьому пристрої був дуже поганий, використовуючи тестовий додаток мультитач (можливо, це був "Тестер телефону", але я не впевнений) Я зміг побачити, що торкання другим пальцем перемістило перше місце торкання багатьох пікселів . Якщо це ваша проблема, ніж це пов'язано з обладнанням, і я не думаю, що ви можете багато для цього зробити :(

Вибачте за мою погану англійську


це не проблема
Green_qaue

ок, була лише думка
Марко Мартінеллі

0

Я вважаю, що ваша проблема полягає в тому, що ви припускаєте, що між оновленнями порядок впорядкування покажчиків залишається однаковим. Це, швидше за все, не буде так.

Уявіть ситуацію, коли ви торкаєтесь пальцем A. Буде вказівникCount () 1, а A - єдиний елемент, про який ви можете запитати. Якщо ви додасте другий палець, pointerCount () буде 2, A буде в індексі 0, B буде в індексі 1. Якщо ви піднімете палець A, pointerCount () знову буде 1, а B буде в індексі 0 . Якщо потім знову торкнутися пальцем A, A буде в індексі 1, а B - в індексі 0 .

Ось чому надається ідентифікатор вказівника, щоб ви могли відслідковувати окремі дотики між оновленнями. Отже, якщо першому дотику пальця B присвоєно ідентифікатор 12, він завжди матиме цей ідентифікатор, навіть коли палець А буде вилучений та повторно доданий.

Тож якщо ваш код визначає дотик біля джойстика для зйомки, він повинен перевірити, чи вже є штрих "зйомки". Якщо ні, то ідентифікатор дотику зйомки повинен запам'ятовуватися в певній змінній учасника, яка зберігається до наступного оновлення. У наступних оновленнях, якщо у вас є дотик "зйомки", перейдіть через покажчики та шукайте вказівник з правильним ідентифікатором, і саме цей вказівник вам потрібно використовувати для відстеження оновлень, а всі інші можна ігнорувати. Те саме для дотику до руху. Коли дотик відпущений, ви очистите ідентифікатор, пов’язаний із цим джойстиком, щоб новий штрих міг взяти під нього контроль.

Навіть якщо дотик, який починається біля одного джойстика, відхиляється далеко від початкового положення дотику, за його ідентифікатором ви все одно можете правильно визначити, яким джойстиком керує. І як хороший побічний ефект, другий бродячий палець біля певного джойстика не матиме жодного ефекту, оскільки джойстик буде прив’язаний до першого пальця, який спрацьовував до його випуску.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.