正しいmultiTouch IDを返す


9

チュートリアルを読んだり、multiTouchに関連するすべての質問をここやStackoverflowから調べたりするために、数え切れないほどの時間を費やしてきました。しかし、これを正しく行う方法がわかりません。私はループを使用してを取得してpointerIdいますが、多くの人がこれを行っているのを見ることはできませんが、それをいくらか機能させるには、これが唯一の方法です。

私の画面には2つのジョイスティックがあります。1つは移動用、もう1つはモンスターシューターのようにスプライトの回転と彼が撃つ角度を制御するためです。これらは両方とも正常に動作します。

私の問題は私の、私はイム撮影と同時に私のスプライトを移動したときにということでtouchingPoint、私の移動のために設定されてtouchingPointいるので、私の撮影のxyで高くなっている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的に私の最初の指と2番目の指の両方を変更します。ボックスにバインドしますが、このボックス内で1本の指を保持している限り、2本目の指が触れた場所に基づいて値を変更します。それは正しい方向に動き、何もエラーを出しません、問題は速度の変化です、それはそれが2つのtouchingPointsを足し合わせるようなものです。

回答:


4

これでうまくいくと思います。

あなたがした1つの間違いは、すべてのイベントのすべてのポインターを反復処理することでした。moveイベントでのみ必要です。

次に、実際にはインデックス値をgetX関数とgetY関数に入れる必要がありますが、そのインデックスに関連付けられたIDを取得して、ゲームオブジェクトへの参照として使用します。ダウンイベント中にジョイスティックにIDを割り当て、次にポインターインデックスを反復処理するときに、インデックスがダウンイベント中にジョイスティックに割り当てたポインターIDに関連付けられているかどうかを確認します。そうである場合は、まだ境界内にあるかどうかを確認し、更新するか無効にします。

私はこのコードをテストしていませんが、自分のコードでメソッドを使用しているので、概念的には機能することを知っています。理解できない問題がある場合は、お知らせください。

まず、以下をジョイスティッククラスに追加する必要があります。

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

onTouchEventをこれに変更します。

public boolean onTouchEvent(MotionEvent event) {


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

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

              }

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

              }

            actionString = "PNTR DOWN";
            break;
        case MotionEvent.ACTION_POINTER_UP:
            if(id == joystick.draggingID)
                joystick.dragging = false;
            if(id ==  joystick.shootingID)
                joystick.shooting = false;
            actionString = "PNTR UP";
            break;
        case MotionEvent.ACTION_CANCEL:
            if(id == joystick.draggingID)
                joystick.dragging = false;
            if(id ==  joystick.shootingID)
                joystick.shooting = false;
            actionString = "CANCEL";
            break;
        case MotionEvent.ACTION_MOVE:
            for(index=0; index<e.getPointerCount(); index++) {
                id=e.getPointerId(index);
                int x = (int) event.getX(index);
                int y = (int) event.getY(index); 
                if(joystick.dragging && id == joystick.draggingId) {
                    if(x > 0 && x < steeringxMesh + joystick.get_joystickBg().getWidth() * 2)
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()) {
                        movingPoint.x = x;
                        movingPoint.y = y;
                    }
                    else
                        dragging = false;
                    }
                }
                else if(joystick.shooting && id == joystick.shootingId){
                    if(x > shootingxMesh - (joystick.get_joystickBg().getWidth()) && x < panel.getWidth()
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()) {
                        shootingPoint.x = x;
                        shootingPoint.y = y;                            
                    }
                    else
                        shooting = false;
                    }
                }
            }
            actionString = "MOVE";
            break;
        }
    }

あなたは私のヒーローです。更新されたコードを質問に追加して、私があなたの助けを借りてそれをどのように解決したかを確認できるようにします!今、私はようやくゲームを終えることができます:D
Green_qaue

7

ほとんど問題はありませんが、X / Yを要求する代わりにポインタIDを使用する必要があります i

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

MotionEventドキュメントから:

個々のポインタがモーションイベント内に表示される順序は定義されていません。したがって、ポインターのポインターインデックスは1つのイベントから次のイベントに変化する可能性がありますが、ポインターのポインターIDは、ポインターがアクティブである限り一定であることが保証されます。getPointerId(int)メソッドを使用してポインターのポインターIDを取得し、ジェスチャー内の後続のすべてのモーションイベント全体でポインターを追跡します。次に、連続するモーションイベントの場合、findPointerIndex(int)メソッドを使用して、そのモーションイベントの特定のポインターIDのポインターインデックスを取得します。

event.getX/Yi同じ順序になる保証がないため、ではなくポインタIDが必要です。

さらに、別の微妙ですが重要な問題があります。getAction()ファミリーの関数がパラメーターを取らないことに注意してください。変だよね?X / Yを取得するには、ポインターIDが必要ですが、実行されたアクションは不要ですか?これは、いくつかの重要なことを示唆しています。

  • 各ポインターのアクションごとにタッチハンドラーへの呼び出しがあり、すべてのポインターのフレームごとに1回の呼び出しではない
  • getX / Yはこれまでのポインタのトレースを調べて最新の値を返しますが、getActionは現在のイベントのみを問い合わせます

つまり、ポインターアクション(下/移動/上)ごとにタッチハンドラーを1 呼び出すことになります。つまり、2本の指の動き= 2通話です。コードの厄介な副作用は、1つのイベントのアクションをすべてのポインターに適用することです...

したがって、トレースをループする代わりに、現在のイベントのみのpid / x / y / actionを取得するだけです(同時に移動する場合は、後で説明したように、ハンドラーへの別の呼び出しを少しだけ取得します)。

イベントを処理するためのコードは次のとおりです。

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

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

  return true;

}

コードに戻るには、次の方法でコードを簡略化できるはずです。ループはなくなりました。それ以外の場合、言及しなかった他の制約がある場合は、いつでも追加できます。これは、DOWNがトリガーされたときにどのポインターIDがどのコントロール(移動/シュート)に使用されるかを追跡し、UPでそれらをリセットすることで機能します。ジェスチャーを使用しないため、switch()をDOWN、UP、MOVE、およびOUTSIDEに制限できます。

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

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


    switch (actionCode)
    {
        case MotionEvent.ACTION_DOWN:
        // on DOWN, figure out whether the player used the moving or shooting control, if any.
        // if so, kept track of which pointer was used, because all following call about that
        // finger touches will use the same pointer id. Also record the current point coordinates.
            actionString = "DOWN";
            try{
                if(x > 0 && x < steeringxMesh + (joystick.get_joystickBg().getWidth() * 2)
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()){
                        movingPoint.x = x;
                        movingPoint.y = y;
                        movePointerId = pid;
                    }
                else if(x > shootingxMesh - (joystick.get_joystickBg().getWidth()) && x < panel.getWidth()
                        && y > yMesh - (joystick.get_joystickBg().getHeight()) && y < panel.getHeight()){
                        shootingPoint.x = x;
                        shootingPoint.y = y;
                        shootingPointerId = pid;
                    }
                }catch(Exception e){

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

    }
}

このすばらしい答えに感謝し、いくつかのことを明確にします。この行sendTouchToGameEngine(pid, actionCode, (int)event.getX(pid), (int)event.getY(pid));が何をするのか、いつそれを呼び出すのか説明していただけますか?
Green_qaue 2012

また、この行を使用する場合:int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;eclipseはsupressWarningを追加するように指示しますが、それは正常ですか?このような詳細な回答の後のすべての質問に申し訳ありません。MotionEventは私にとって非常に新しく、何らかの理由でロジックを理解できません
Green_qaue

更新されたコードを追加しました。これは、どうすればいいのか本当にわからpidないので、ループがまだそこにありますi。取得する必要があるため、ループがないと動作しません。
Green_qaue 2012

@Max:sendTouchToGameEngineは、このイベントをゲームエンジンキューに追加して、次の更新時に処理されるようにするための呼び出しです。キューを使用して入力イベントをポーリングしない場合、TouchEventがUIスレッドから発生し、おそらくゲームエンジンが更新されるため、呼び出し機能が予期しない方法でゲームエンジンの状態を乱すリスクがあります。別のスレッドで実行
ADB

@Max:残念ながら警告については知りません。どれか教えていただけますか?
ADB、

0

このコードでは、少し奇妙なことが起こっています。私はAndroidを使用していないので、多分これはそうではないと思います。しかし、私はあなたが2つの異なる方法でポジションを2度獲得していることに気づきました。

最初に、pointerCountループの開始時に次のように取得します。

(int) event.getX(i)

次に、switchステートメント内で次のように取得します。

(int) event.getX(id)

からiへの切り替えに注意してくださいid

前者の方法だと思います。のすべてのインスタンスを置き換えて、最初に設定した(int) event.getX(id)xを使用することをお勧めします。同様にに交換(int) event.getY(id)yます。

これらのパーツを交換してみてください。

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

次に、スイッチ内でこれを使用します。

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

2
これがなぜ役に立たず、反対票に値しないのかについてコメントしてください。それは礼儀正しいことです。
MichaelHouse

反対票を投じる場合は同意します。彼に理由を教えてください。あなたは正しい前者の方法からです。私は約100万の異なるものを試したので、それを削除するのを忘れました。
Green_qaue 2012

それを反映するように質問のコードを更新できますか?私はあなたがの設定を削除見るxy、しかし、あなたはまだで位置を取得しているid後で。
MichaelHouse

ああ、あなたの答えを理解するのにしばらくかかりました。しかし、これはどのように機能しますか?同じ変数(x=event.getX(i))を使用する場合、1より大きい場合はx常に2つの値を格納する必要がpointerCountありますか?そして、それは正しく感じません。
Green_qaue 2012

1
私が理解してeventいることから、本当にイベントのリストです。あなたがしているようにリストを通過することにより、さまざまなイベントにアクセスします。したがって、x使用する2番目のイベントの位置にアクセスするにはevent.getX(1)(0から開始しているため)。複数xのがあり、パラメータを使用して、どちらが必要かを伝えます。ループを使用してすべてのイベントをforループしているので、その番号を関心のある現在のイベントとして使用します。コード変更の提案を参照してください。
MichaelHouse

0

Huawei U8150で同様の問題が発生しました。そのデバイスでの2本の指のマルチタッチは、マルチタッチテストアプリを使用して本当に悪かった(おそらく「電話テスター」だったのかもしれませんが、よくわかりません)2本目の指でタッチすると、多くのピクセルの最初のタッチスポットが移動したことがわかりました。それがハードウェア関連の問題よりも問題であり、それに対して多くのことを行うことができないと思います:(

英語が下手でごめんなさい


それは問題ではありません
Green_qaue 2012

わかりました、単なる考えでした
マルコマルティネッリ2012

0

私はあなたの問題は、ポインタが配置される順序が同じままであると更新の間で仮定していることだと信じています。それはおそらくそうではないでしょう。

指Aでタッチする状況を想像してみてください。pointerCount()は1になり、質問できる要素はAだけになります。2本目の指を追加すると、pointerCount()は2、Aはインデックス0、Bはインデックス1になります。指Aを上げると、pointerCount()は再び1になり、Bはインデックス0になります。。その後、もう一度指Aでタッチすると、Aはインデックス1になり、Bはインデックス0になります。

これがポインタIDが提供される理由で、更新間の個々のタッチを追跡できます。したがって、指Bの最初のタッチにID 12が割り当てられている場合、指Aを削除して再度追加した場合でも、IDは常にそのIDになります。

したがって、コードがシューティングジョイスティックの近くのタッチを識別した場合、進行中の「シューティング」タッチが既にあるかどうかを確認する必要があります。そうでない場合、シューティングタッチのIDは、次の更新まで持続するいくつかのメンバー変数に記憶されている必要があります。後続の更新では、「シューティング」タッチが進行中の場合、ポインターを反復処理して正しいIDのポインターを探します。これは、更新の追跡に使用する必要があるポインターであり、他のすべては無視できます。ムーブメントタッチも同様です。タッチが解放されると、そのジョイスティックに関連付けられているIDをクリアして、新しいタッチがそれを制御できるようにします。

1つのジョイスティックの近くで始まるタッチが元のタッチ位置から離れていても、そのIDによって、どのジョイスティックが制御しているかを正しく識別できます。また、良い副作用として、特定のジョイスティックの近くにある2本目の浮遊指は何の効果もありません。ジョイスティックは、リリースされるまでトリガーした最初の指にバインドされるためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.