restituendo l'ID multiTouch corretto


9

Ho trascorso innumerevoli ore a leggere tutorial e ad esaminare tutte le domande relative a multiTouch da qui e StackOverflow. Ma non riesco proprio a capire come farlo correttamente. Uso un ciclo per ottenere il mio pointerId, non vedo molte persone che lo fanno, ma è l'unico modo in cui sono riuscito a farlo funzionare un po '.

Ho due joystick sul mio schermo, uno per muoversi e uno per controllare la rotazione dei miei folletti e l'angolazione che spara, come in Monster Shooter. Entrambi funzionano bene.

Il mio problema è che quando sposto il mio sprite contemporaneamente allo scatto di Im, il mio touchingPointper il mio movimento è impostato su touchingPointdella mia ripresa, poiché xe yè più alto sulla touchingPointdella mia ripresa ( moving-sticksul lato sinistro dello schermo, shooting-sticksul lato destro) , il mio sprite accelera, questo crea un cambiamento indesiderato nella velocità del mio sprite.

è così che l'ho risolto con il tuo aiuto! questo è per chiunque possa incorrere in un problema simile:

    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);

Non pubblicherei questo codice se non avessi una perdita assoluta di ciò che sto facendo di sbagliato. Semplicemente non riesco a capire bene come funziona multiTouching.

movingPointcambia sostanzialmente sia per il mio primo che per il secondo dito. Lo lego a una scatola, ma finché tengo premuto un dito all'interno di questa casella, cambia il suo valore in base a dove tocca il mio secondo dito. Si muove nella giusta direzione e nulla dà un errore, il problema è il cambio di velocità, è quasi come se sommasse i due punti toccanti.

Risposte:


4

Penso che questo funzionerà per te.

Un errore che hai fatto è stato iterare su tutti i puntatori per ogni evento. È necessario solo per gli eventi di movimento.

In secondo luogo, devi effettivamente inserire il valore dell'indice nelle funzioni getX e getY, ma ottieni l'id associato a tale indice da utilizzare come riferimento agli oggetti di gioco. Assegnate un ID al vostro joystick durante l'evento Down e quindi quando ripetete gli indici del puntatore, verificate se l'indice è associato all'ID puntatore assegnato al joystick durante l'evento Down. Se lo è, controlla se è ancora nei limiti e aggiornalo o disabilitalo.

Non sto testando questo codice, ma so che funziona in modo concettuale perché uso il metodo nel mio codice. Fammi sapere se ci sono problemi che non riesci a capire.

Innanzitutto, dovrai aggiungere quanto segue alla tua classe di joystick.

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

Cambia il tuo onTouchEvent in questo.

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;
        }
    }

signore, siete il mio eroe. Aggiungerò il codice aggiornato alla mia domanda in modo da poter vedere come l'ho risolto con il tuo aiuto! ora posso finalmente finire il mio gioco: D
Green_qaue,

7

Stai quasi andando bene, ma dovresti usare il tuo ID puntatore per richiedere X / Y invece di i

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

Dalla documentazione MotionEvent:

L'ordine in cui i singoli puntatori compaiono all'interno di un evento di movimento non è definito. Pertanto, l'indice del puntatore di un puntatore può cambiare da un evento all'altro, ma si garantisce che l'id del puntatore di un puntatore rimanga costante fintanto che il puntatore rimane attivo. Utilizzare il metodo getPointerId (int) per ottenere l'ID puntatore di un puntatore per tenerlo traccia di tutti gli eventi di movimento successivi in ​​un gesto. Quindi, per eventi di movimento successivi, utilizzare il metodo findPointerIndex (int) per ottenere l'indice del puntatore per un determinato ID puntatore in quell'evento di movimento.

event.getX/Yrichiede un ID puntatore, no i, perché non c'è garanzia che saranno nello stesso ordine.

Inoltre, c'è un altro problema sottile ma importante. Notare come la famiglia di funzioni getAction () non accetta un parametro. È strano, vero? Ottenere X / Y richiede l'ID puntatore, ma non l'azione eseguita? Questo suggerisce un paio di cose importanti:

  • ricevi una chiamata al tuo gestore di tocco per ogni azione di ciascun puntatore e non una singola chiamata per frame per tutti i puntatori
  • getX / Y guarda la traccia del puntatore finora e restituisce l'ultimo valore, mentre getAction richiede solo l'evento corrente

Ciò significa che ricevi una chiamata al gestore del tocco per azione del puntatore (giù / sposta / su). Quindi due dita in movimento = 2 chiamate. Un brutto effetto collaterale del tuo codice è che applica l'azione di un evento a tutti i puntatori ...

Quindi, invece di passare in rassegna le tracce, ottieni semplicemente l'azione pid / x / y / solo per l'evento corrente (per il movimento simultaneo, riceverai un'altra chiamata al tuo gestore un po 'più tardi, come ho detto)

Ecco il mio codice per gestire gli eventi:

 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;

}

Per tornare al tuo codice, dovresti essere in grado di semplificarlo nel modo seguente. Il ciclo è sparito, altrimenti, se hai altre contraddizioni che non hai menzionato, puoi sempre aggiungerlo di nuovo. Funziona monitorando quale ID puntatore viene utilizzato per quale controllo (sposta / spara) quando DOWN viene attivato e ripristinandoli su UP. Dato che non usi i gesti, puoi limitare l'interruttore () su GIÙ, SU, SPOSTA ed ESTERNO.

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";

    }
}

grazie per questa grande risposta, chiarisce davvero un paio di cose. Potresti semplicemente spiegare cosa fa questa linea sendTouchToGameEngine(pid, actionCode, (int)event.getX(pid), (int)event.getY(pid));e quando la chiami?
Green_qaue,

anche quando uso questa riga: int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;eclipse mi dice di aggiungere un supress Avvertenza, è normale? Ci scusiamo per tutte le domande dopo una risposta così dettagliata. MotionEvent è molto nuovo per me e non riesco a capire la logica per qualche motivo
Green_qaue

aggiunto codice aggiornato, come puoi vedere non so davvero cosa fare pid, e il loop è ancora lì, non funzionerà senza di esso, dal momento che ho bisogno di ottenere i.
Green_qaue,

@Max: sendTouchToGameEngine è semplicemente una chiamata per aggiungere questo evento alla mia coda del motore di gioco da elaborare durante il prossimo aggiornamento. Se non si utilizza una coda per eseguire il polling dell'evento di input, si corre il rischio che la funzione di chiamata interferisca con lo stato del motore di gioco in modi inaspettati, poiché TouchEvent proviene dal thread dell'interfaccia utente e, presumibilmente, dall'aggiornamento del motore di gioco viene eseguito in un thread diverso
ADB

@Max: purtroppo non conosco l'avvertimento. Puoi dirci quale è?
ADB,

0

C'è un po 'di stranezza con questo codice. Non uso Android, quindi forse sto pensando che non lo è. Tuttavia ho notato che stai ottenendo la posizione due volte, in due modi diversi:

Per prima cosa lo ottieni in questo modo all'inizio del tuo pointerCountciclo:

(int) event.getX(i)

Quindi, all'interno della tua istruzione switch, la ottieni in questo modo:

(int) event.getX(id)

Si noti che si passa dall'uso iall'utilizzo id.

Suppongo che dovrebbe essere il metodo precedente. Consiglio di sostituire tutte le istanze di (int) event.getX(id)e utilizzare il valore ximpostato all'inizio. Allo stesso modo, passare (int) event.getY(id)a y.

Prova a scambiare queste parti:

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

Quindi all'interno del tuo switch usa questo:

    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
Vuoi commentare perché questo non è utile e degno di un voto negativo? È la cosa educata da fare.
MichaelHouse

D'accordo, se voti in negativo. Fagli sapere perché. Hai ragione è da un precedente metodo. Ho provato circa un milione di cose diverse, quindi ho dimenticato di rimuoverlo.
Green_qaue,

Puoi aggiornare il codice nella tua domanda per riflettere quello? Vedo che hai rimosso l'impostazione di xe y, ma stai ancora ottenendo la posizione in idseguito.
MichaelHouse

aah, mi ci è voluto un po 'per capire la tua risposta. ma come funzionerà? se uso la stessa variabile ( x=event.getX(i)), allora dovrò xmemorizzare 2 valori ogni volta che pointerCountè più di 1, giusto? E questo non sembra giusto.
Green_qaue,

1
Da quello che ho capito eventè davvero un elenco di eventi. Puoi accedere ai diversi eventi scorrendo l'elenco come stai facendo. Quindi, per accedere alla xposizione per il secondo evento che usi event.getX(1)(dato che stiamo iniziando da 0). Ci sono più xs, stai usando il parametro per dirlo su quale vuoi. Stai eseguendo il looping di tutti gli eventi con il tuo forloop, quindi utilizza quel numero come evento corrente a cui sei interessato. Vedi il mio suggerimento di modifica del codice.
MichaelHouse

0

Ho avuto un problema simile una volta su un Huawei U8150. Il multitouch a due dita su quel dispositivo era davvero pessimo, usando un'app di test multitouch (forse era "Phone Tester" ma non sono sicuro) Sono stato in grado di vedere che toccando con il secondo dito si spostava il primo punto di tocco di molti pixel . Se questo è il tuo problema rispetto all'hardware e non penso che tu possa fare molto per questo :(

Scusa per il mio cattivo inglese


non è questo il problema
Green_qaue,

ok, era solo un pensiero
Marco Martinelli,

0

Credo che il tuo problema sia che stai presupponendo che, tra gli aggiornamenti, l'ordine in cui sono disposti i puntatori rimanga lo stesso. Molto probabilmente non sarà così.

Immagina una situazione in cui tocchi con il dito A. Ci sarà un pointerCount () di 1 e A sarà l'unico elemento a cui puoi chiedere. Se aggiungi un secondo dito, pointerCount () sarà 2, A sarà all'indice 0, B sarà all'indice 1. Se sollevi il dito A, pointerCount () sarà di nuovo 1 e B sarà all'indice 0 . Se poi tocchi di nuovo con il dito A, A sarà all'indice 1 e B sarà all'indice 0 .

Questo è il motivo per cui viene fornito l'ID puntatore, in modo da poter tenere traccia dei singoli tocchi tra gli aggiornamenti. Quindi, se al primo tocco del dito B viene assegnato l'ID 12, avrà sempre quell'ID, anche quando il dito A viene rimosso e aggiunto di nuovo.

Pertanto, se il codice identifica un tocco vicino al joystick di scatto, deve verificare se è già in corso un tocco di "scatto". In caso contrario, l'ID del tocco di ripresa dovrebbe essere ricordato in alcune variabili membro che persiste al prossimo aggiornamento. Negli aggiornamenti successivi, se è in corso un tocco di "ripresa", scorrere i puntatori e cercare il puntatore con l'ID giusto, ed è quel puntatore che è necessario utilizzare per tenere traccia degli aggiornamenti e tutti gli altri possono essere ignorati. Lo stesso per il tocco di movimento. Quando il tocco viene rilasciato, si cancella l'ID associato a quel joystick in modo che un nuovo tocco possa prenderne il controllo.

Anche se un tocco che inizia vicino a un joystick si allontana dalla posizione originale del tocco, dal suo ID è ancora possibile identificare correttamente quale joystick sta controllando. E come un piacevole effetto collaterale, un secondo dito randagio vicino a un particolare joystick non avrà alcun effetto, perché il joystick sarà legato al primo dito che lo ha attivato fino a quando non viene rilasciato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.