renvoi de l'identifiant multiTouch correct

9

J'ai passé d'innombrables heures à lire des tutoriels et à regarder toutes les questions liées à multiTouch d'ici et à Stackoverflow. Mais je n'arrive pas à comprendre comment le faire correctement. J'utilise une boucle pour obtenir mon pointerId, je ne vois pas beaucoup de gens faire cela, mais c'est la seule façon que j'ai réussi à faire fonctionner quelque peu.

J'ai deux joysticks sur mon écran, un pour se déplacer et un pour contrôler la rotation de mes sprites et l'angle qu'il tire, comme dans Monster Shooter. Ces deux fonctionnent bien.

Mon problème est que lorsque je déplace mon sprite en même temps que la prise de vue Im, mon touchingPointpour mon mouvement est réglé sur celui touchingPointde ma prise de vue, car le xet yest plus haut sur la prise touchingPointde vue ( moving-sticksur le côté gauche de l'écran, shooting-sticksur le côté droit) , mon sprite accélère, cela crée un changement de vitesse indésirable pour mon sprite.

c'est ainsi que je l'ai résolu avec votre aide! c'est pour toute personne qui pourrait rencontrer un problème similaire:

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

Je ne posterais pas autant de code si je n'étais pas à une perte absolue de ce que je fais mal. Je n'arrive tout simplement pas à bien comprendre le fonctionnement du multi-touch.

movingPointchange fondamentalement pour mon premier et mon deuxième doigt. Je le lie à une boîte, mais aussi longtemps que je tiens un doigt dans cette boîte, il change sa valeur en fonction de l'endroit où mon deuxième doigt touche. Il se déplace dans la bonne direction et rien ne donne d'erreur, le problème est le changement de vitesse, c'est presque comme s'il additionne les deux points de contact.

Max
la source

Réponses:

4

Je pense que cela fonctionnera pour vous.

Une erreur que vous avez commise a été d'itérer tous les pointeurs pour chaque événement. Il n'est nécessaire que pour les événements de déplacement.

Deuxièmement, vous devez réellement mettre la valeur d'index dans les fonctions getX et getY, mais vous obtenez l'id associé à cet index pour l'utiliser comme référence à vos objets de jeu. Vous attribuez un identifiant à votre joystick pendant l'événement Down, puis lors de l'itération dans vos index de pointeur, vérifiez si l'index est associé à l'ID de pointeur que vous avez attribué au joystick pendant l'événement Down. Si c'est le cas, vérifiez s'il est toujours dans les limites et mettez-le à jour ou désactivez-le.

Je ne teste pas ce code, mais je sais qu'il fonctionne dans le concept car j'utilise la méthode dans mon propre code. Faites-moi savoir s'il y a des problèmes que vous ne pouvez pas résoudre.

Tout d'abord, vous devrez ajouter ce qui suit à votre classe de joystick.

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

Changez votre onTouchEvent en ceci.

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;
        }
    }
Reynard
la source
vous monsieur, êtes mon héros. Je vais ajouter le code mis à jour à ma question afin que vous puissiez voir comment je l'ai résolu avec votre aide! maintenant je peux enfin terminer mon jeu: D
Green_qaue
7

Vous avez presque raison, mais vous devez utiliser votre identifiant de pointeur pour demander le X / Y au lieu de i

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

Dans la documentation MotionEvent:

L'ordre dans lequel les pointeurs individuels apparaissent dans un événement de mouvement n'est pas défini. Ainsi, l'index de pointeur d'un pointeur peut changer d'un événement à l'autre, mais l'ID de pointeur d'un pointeur est garanti pour rester constant tant que le pointeur reste actif. Utilisez la méthode getPointerId (int) pour obtenir l'ID de pointeur d'un pointeur pour le suivre à travers tous les événements de mouvement suivants dans un geste. Ensuite, pour les événements de mouvement successifs, utilisez la méthode findPointerIndex (int) pour obtenir l'index du pointeur pour un ID de pointeur donné dans cet événement de mouvement.

event.getX/Ynécessitent un identifiant de pointeur, non i, car il n'y a aucune garantie qu'ils seront dans le même ordre.

De plus, il existe un autre problème subtil mais important. Remarquez comment la famille de fonctions getAction () ne prend pas de paramètre. C'est un peu bizarre, non? Obtenir X / Y nécessite l'ID du pointeur, mais pas l'action effectuée? Cela fait allusion à quelques éléments importants:

  • vous recevez un appel à votre gestionnaire tactile pour chaque action de chaque pointeur et pas un seul appel par image pour tous les pointeurs
  • getX / Y examine la trace du pointeur jusqu'à présent et renvoie la dernière valeur, tandis que getAction ne demande que l'événement en cours

Cela signifie donc que vous recevez un appel vers votre gestionnaire tactile par action de pointeur (bas / déplacer / haut). Donc deux doigts en mouvement = 2 appels. Un effet secondaire désagréable de votre code est qu'il applique l'action d'un événement à tous les pointeurs ...

Ainsi, au lieu de parcourir les traces, obtenez simplement l'action pid / x / y / pour l'événement en cours uniquement (pour un mouvement simultané, vous obtiendrez un autre appel à votre gestionnaire un peu plus tard, comme je l'ai dit)

Voici mon code pour gérer les événements:

 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;

}

Pour revenir à votre code, vous devriez pouvoir le simplifier de la manière suivante. La boucle a disparu, sinon, si vous avez d'autres contraintes que vous n'avez pas mentionnées, vous pouvez toujours les rajouter. Il fonctionne en suivant quel identifiant de pointeur est utilisé pour quel contrôle (déplacer / tirer) lorsque DOWN est déclenché et en les réinitialisant sur UP. Puisque vous n'utilisez pas de gestes, vous pouvez restreindre votre commutateur () à DOWN, UP, MOVE et EXTERIEUR.

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

    }
}
ADB
la source
merci pour cette excellente réponse, clarifie vraiment deux ou trois choses. Pourriez-vous simplement expliquer ce que fait cette ligne sendTouchToGameEngine(pid, actionCode, (int)event.getX(pid), (int)event.getY(pid));et quand vous l'appelez?
Green_qaue
aussi quand j'utilise cette ligne: int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;eclipse me dit d'ajouter un supressWarning, est-ce normal? Désolé pour toutes les questions après une réponse aussi détaillée. MotionEvent est très nouveau pour moi et ne peut pas comprendre la logique pour une raison quelconque
Green_qaue
ajout de code mis à jour, comme vous pouvez le voir, je ne sais pas vraiment quoi faire pid, et la boucle est toujours là, ne fonctionnera pas sans, car j'ai besoin de l'obtenir i.
Green_qaue
@Max: le sendTouchToGameEngine est simplement un appel pour ajouter cet événement à ma file d'attente de moteur de jeu pour être traité lors de la prochaine mise à jour. Si vous n'utilisez pas de file d'attente pour interroger votre événement d'entrée, vous courez le risque que votre fonction d'appel perturbe l'état du moteur de jeu de manière inattendue, car le TouchEvent provient du thread d'interface utilisateur et, vraisemblablement, de la mise à jour de votre moteur de jeu. s'exécute dans un thread différent
ADB
@Max: Je ne connais malheureusement pas l'avertissement. Pouvez-vous nous dire lequel c'est?
ADB
0

Il y a un peu de bizarrerie avec ce code. Je n'utilise pas Android, alors peut-être que je pense que ce n'est pas le cas. Cependant, j'ai remarqué que vous obtenez le poste deux fois, de deux manières différentes:

D'abord, vous l'obtenez comme ceci au début de votre pointerCountboucle:

(int) event.getX(i)

Ensuite, à l'intérieur de votre instruction switch, vous obtenez ce qui suit:

(int) event.getX(id)

Notez que vous passez de l'utilisation ià l'utilisation id.

Je suppose que c'est censé être l'ancienne méthode. Je recommande de remplacer toutes les instances de (int) event.getX(id)et d'utiliser la valeur que xvous avez définie au début. De même, permuter (int) event.getY(id)vers y.

Essayez d'échanger ces parties:

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

Ensuite, à l'intérieur de votre commutateur, utilisez ceci:

    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
la source
2
Voulez-vous commenter pourquoi cela n'est pas utile et mérite un vote négatif? C'est la chose polie à faire.
MichaelHouse
D'accord, si vous votez contre. Faites-lui savoir pourquoi. Vous avez raison, c'est d'une ancienne méthode. J'ai essayé environ un million de choses différentes, alors j'ai juste oublié de le supprimer.
Green_qaue
Pouvez-vous mettre à jour le code de votre question pour refléter cela? Je vois que vous avez supprimé le paramètre de xet y, mais vous obtenez toujours la position idplus tard.
MichaelHouse
aah, il m'a fallu un certain temps pour comprendre votre réponse. mais comment cela fonctionnera-t-il? si j'utilise la même variable ( x=event.getX(i)), alors xdevra stocker 2 valeurs chaque fois pointerCountest plus que 1, correct? Et ce dosnt se sent bien.
Green_qaue
1
D'après ce que je comprends, eventc'est vraiment une liste d'événements. Vous accédez aux différents événements en parcourant la liste comme vous le faites. Donc, pour accéder à la xposition du deuxième événement que vous utilisez event.getX(1)(puisque nous commençons à 0). Il y a plusieurs xs, vous utilisez le paramètre pour lui dire lequel vous voulez. Vous parcourez tous les événements avec votre forboucle, alors utilisez ce numéro comme événement actuel qui vous intéresse. Voir ma suggestion de changement de code.
MichaelHouse
0

J'ai eu un problème similaire une fois sur un Huawei U8150. Le multitouch à deux doigts sur cet appareil était vraiment mauvais, en utilisant une application de test multitouch (peut-être que c'était "Phone Tester" mais je ne suis pas sûr) J'ai pu voir que toucher avec le deuxième doigt déplaçait le 1er point de contact de plusieurs pixels . Si c'est votre problème, c'est lié au matériel et je ne pense pas que vous puissiez en faire beaucoup :(

Désolé pour mon mauvais anglais

Marco Martinelli
la source
ce n'est pas le problème
Green_qaue
ok, c'était juste une pensée
Marco Martinelli
0

Je crois que votre problème est que vous supposez qu'entre les mises à jour, l'ordre dans lequel les pointeurs sont disposés reste le même. Ce ne sera probablement pas le cas.

Imaginez une situation où vous touchez avec le doigt A. Il y aura un pointeurCount () de 1, et A sera le seul élément que vous pouvez demander. Si vous ajoutez un deuxième doigt, le pointeurCount () sera 2, A sera à l'index 0, B sera à l'index 1. Si vous relevez le doigt A, pointerCount () sera à nouveau 1 et B sera à l'index 0 . Si vous touchez à nouveau avec le doigt A, A sera à l'index 1 et B sera à l'index 0 .

C'est pourquoi l'ID du pointeur est fourni, afin que vous puissiez suivre les contacts individuels entre les mises à jour. Donc, si la première touche du doigt B se voit attribuer l'ID 12, il aura toujours cet ID, même lorsque le doigt A est retiré et rajouté.

Donc, si votre code identifie une touche près de votre joystick de prise de vue, il doit vérifier s'il y a déjà une touche «prise de vue» en cours. Si ce n'est pas le cas, l'ID de la prise de vue doit être mémorisé dans une variable membre qui persiste jusqu'à la prochaine mise à jour. Dans les mises à jour suivantes, si vous avez une touche de `` prise de vue '' en cours, parcourez les pointeurs et recherchez le pointeur avec le bon ID, et c'est ce pointeur que vous devez utiliser pour suivre les mises à jour et tous les autres peuvent être ignorés. Idem pour la touche mouvement. Lorsque le toucher est relâché, vous effacez l'ID associé à ce joystick pour qu'un nouveau toucher puisse en prendre le contrôle.

Même si un toucher qui commence à proximité d'un joystick s'éloigne de la position de toucher d'origine, par son ID, vous pouvez toujours identifier correctement le joystick qu'il contrôle. Et comme bel effet secondaire, un deuxième doigt errant près d'un joystick particulier n'aura aucun effet, car le joystick sera lié au premier doigt qui l'a déclenché jusqu'à ce qu'il soit relâché.

MrCranky
la source