mengembalikan id multi Touch yang benar

9

Saya telah menghabiskan banyak waktu untuk membaca tutorial dan melihat setiap pertanyaan yang terkait dengan multiTouch dari sini dan Stackoverflow. Tapi saya tidak tahu bagaimana melakukan ini dengan benar. Saya menggunakan loop untuk mendapatkan pointerId, saya tidak melihat banyak orang melakukan ini tetapi satu-satunya cara saya berhasil membuatnya agak berfungsi.

Saya memiliki dua joystick di layar saya, satu untuk bergerak dan satu untuk mengendalikan rotasi sprite saya dan sudut yang dia tembak, seperti di Monster Shooter. Keduanya bekerja dengan baik.

Masalah saya adalah ketika saya Memindahkan sprite saya pada saat yang sama dengan pemotretan Im, touchingPointgerakan saya untuk saya diatur ke touchingPointpemotretan saya, karena xdan ylebih tinggi pada touchingPointpemotretan saya ( moving-stickdi sisi kiri layar, shooting-stickdi sisi kanan) , sprite saya mempercepat, ini menciptakan perubahan kecepatan yang tidak diinginkan untuk sprite saya.

beginilah cara saya menyelesaikannya dengan bantuan Anda! ini untuk siapa saja yang mungkin mengalami masalah serupa:

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

Tidak akan memposting kode sebanyak ini jika saya tidak benar-benar kehilangan apa yang saya lakukan salah. Saya benar-benar tidak bisa mendapatkan pemahaman yang baik tentang cara kerja multiTouching.

pada dasarnya movingPointperubahan untuk jari pertama dan kedua saya. Saya mengikatnya ke sebuah kotak, tetapi selama saya memegang satu jari di dalam kotak ini, itu mengubah nilainya berdasarkan di mana jari kedua saya menyentuh. Itu bergerak ke arah yang benar dan tidak ada yang memberikan kesalahan, masalahnya adalah perubahan kecepatan, hampir seperti menambahkan dua touchingPoints.

Maks
sumber

Jawaban:

4

Saya pikir ini akan berhasil untuk Anda.

Satu kesalahan yang Anda buat adalah mengulangi semua petunjuk untuk setiap acara. Ini hanya diperlukan untuk memindahkan acara.

Kedua, Anda benar-benar perlu memasukkan nilai indeks ke fungsi getX dan getY, tetapi Anda mendapatkan id yang terkait dengan indeks itu untuk digunakan sebagai referensi ke objek game Anda. Anda menetapkan id ke joystick Anda selama Acara Turun dan kemudian saat iterasi melalui indeks pointer Anda, periksa apakah indeks dikaitkan dengan id pointer yang Anda tetapkan untuk joystick selama acara Down. Jika ya, periksa apakah masih dalam batas dan perbarui atau nonaktifkan.

Saya tidak menguji kode ini, tetapi saya tahu itu berfungsi dalam konsep karena saya menggunakan metode dalam kode saya sendiri. Beri tahu saya jika ada masalah apa pun yang tidak dapat Anda pecahkan.

Pertama, Anda harus menambahkan yang berikut ke kelas joystick Anda.

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

Ubah onTouchEvent Anda menjadi ini.

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
sumber
Anda tuan, adalah pahlawan saya. Saya akan menambahkan kode yang diperbarui ke pertanyaan saya sehingga Anda dapat melihat bagaimana saya menyelesaikannya dengan bantuan Anda! sekarang saya akhirnya bisa menyelesaikan permainan saya: D
Green_qaue
7

Anda hampir melakukannya dengan benar, tetapi Anda harus menggunakan id penunjuk untuk meminta X / Y alih-alih i

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

Dari dokumentasi MotionEvent:

Urutan di mana pointer individu muncul dalam suatu peristiwa gerakan tidak ditentukan. Dengan demikian indeks pointer dari sebuah pointer dapat berubah dari satu peristiwa ke peristiwa berikutnya tetapi id pointer dari suatu pointer dijamin tetap konstan selama pointer tetap aktif. Gunakan metode getPointerId (int) untuk mendapatkan id pointer dari pointer untuk melacaknya di semua peristiwa gerakan berikutnya dalam gerakan. Kemudian untuk acara gerak berturut-turut, gunakan metode findPointerIndex (int) untuk mendapatkan indeks penunjuk untuk id penunjuk yang diberikan dalam peristiwa gerakan itu.

event.getX/Ymemerlukan id pointer, bukan i, karena tidak ada jaminan mereka akan berada dalam urutan yang sama.

Selain itu, ada masalah lain yang halus namun penting. Perhatikan bagaimana rangkaian fungsi getAction () tidak mengambil parameter. Agak aneh, kan? Mendapatkan X / Y membutuhkan id pointer, tetapi bukankah tindakan dilakukan? Itu mengisyaratkan beberapa hal penting:

  • Anda mendapatkan panggilan ke Anda menyentuh handler untuk setiap tindakan dari masing-masing pointer dan bukan panggilan tunggal per frame untuk semua pointer
  • getX / Y melihat jejak pointer sejauh ini dan mengembalikan nilai terbaru, sementara getAction menanyakan tentang peristiwa saat ini saja

Jadi itu berarti Anda mendapatkan satu panggilan ke penangan sentuh Anda per tindakan pointer (turun / bergerak / naik). Jadi dua jari bergerak = 2 panggilan. Efek samping buruk dari kode Anda adalah bahwa ia menerapkan aksi dari satu peristiwa ke semua petunjuk ...

Jadi, alih-alih mengulangi jejak, cukup dapatkan tindakan pid / x / y / hanya untuk acara saat ini (untuk gerakan simultan, Anda akan mendapatkan panggilan lain ke handler Anda sedikit kemudian, seperti yang saya katakan)

Ini kode saya untuk menangani acara:

 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;

}

Untuk kembali ke kode Anda, Anda harus dapat menyederhanakannya dengan cara berikut. Loopnya hilang, jika tidak, jika Anda memiliki kendala lain yang tidak Anda sebutkan, Anda selalu dapat menambahkannya kembali. Ini bekerja dengan melacak id penunjuk mana yang digunakan untuk kontrol mana (bergerak / menembak) ketika BAWAH dipicu dan mengatur ulang mereka di ATAS. Karena Anda tidak menggunakan gerakan, Anda dapat membatasi sakelar () ke BAWAH, ATAS, MOVE dan LUAR.

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
sumber
terima kasih atas jawaban yang luar biasa ini, benar-benar menjelaskan beberapa hal. Bisakah Anda jelaskan apa yang sendTouchToGameEngine(pid, actionCode, (int)event.getX(pid), (int)event.getY(pid));dilakukan baris ini dan kapan Anda menyebutnya?
Green_qaue
juga ketika saya menggunakan baris ini: int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;gerhana memberitahu saya untuk menambahkan supressWarning, apakah itu normal? Maaf untuk semua pertanyaan setelah jawaban terperinci seperti itu. MotionEvent sangat baru bagi saya, dan tidak dapat memahami logika karena beberapa alasan
Green_qaue
menambahkan kode yang diperbarui, seperti yang Anda lihat, saya tidak tahu apa yang harus dilakukan pid, dan loop masih ada, tidak akan berfungsi tanpanya, karena saya harus mendapatkannya i.
Green_qaue
@ Max: sendTouchToGameEngine hanyalah panggilan untuk menambahkan acara ini ke antrean mesin game saya untuk diproses selama pembaruan berikutnya. Jika Anda tidak menggunakan antrian untuk mensurvei acara masukan Anda, Anda menjalankan risiko fungsi panggilan Anda mengacaukan kondisi mesin game dengan cara yang tidak terduga, karena TouchEvent berasal dari utas UI dan, mungkin, pembaruan mesin game Anda berjalan di utas yang berbeda
ADB
@ Max: Sayangnya saya tidak tahu tentang peringatan itu. Bisakah Anda memberi tahu kami yang mana?
ADB
0

Ada sedikit keanehan yang terjadi dengan kode ini. Saya tidak menggunakan Android, jadi mungkin saya berpikir ini adalah sesuatu yang bukan. Namun saya perhatikan Anda mendapatkan posisi itu dua kali, dengan dua cara berbeda:

Pertama Anda mendapatkannya seperti ini di awal pointerCountloop Anda :

(int) event.getX(i)

Kemudian, di dalam statement switch Anda, Anda mendapatkannya seperti ini:

(int) event.getX(id)

Perhatikan bahwa Anda beralih dari menggunakan ike menggunakan id.

Saya berasumsi itu seharusnya menjadi metode yang pertama. Saya sarankan mengganti semua instance (int) event.getX(id)dan menggunakan nilai yang xAnda atur di awal. Demikian juga bertukar (int) event.getY(id)key .

Cobalah menukar bagian-bagian ini:

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

Kemudian di dalam sakelar Anda gunakan ini:

    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
sumber
2
Ingin mengomentari mengapa ini tidak berguna dan tidak layak untuk downvote? Itu hal yang sopan untuk dilakukan.
MichaelHouse
Setuju, jika Anda memilih. Biarkan dia tahu kenapa. Anda benar dari metode sebelumnya. Saya sudah mencoba sekitar satu juta hal yang berbeda jadi saya hanya lupa untuk menghapusnya.
Green_qaue
Bisakah Anda memperbarui kode dalam pertanyaan Anda untuk mencerminkan hal itu? Saya melihat Anda menghapus pengaturan xdan y, tetapi Anda masih mendapatkan posisi idnanti.
MichaelHouse
aah, butuh waktu beberapa saat untuk memahami jawaban Anda. tetapi bagaimana cara kerjanya? jika saya menggunakan variabel yang sama ( x=event.getX(i)), maka xharus menyimpan 2 nilai setiap kali pointerCountlebih dari 1, benar? Dan itu terasa tidak enak.
Green_qaue
1
Dari apa yang saya mengerti eventbenar-benar daftar acara. Anda mengakses berbagai acara dengan menelusuri daftar seperti yang Anda lakukan. Jadi untuk mengakses xposisi untuk acara kedua yang Anda gunakan event.getX(1)(karena kita mulai dari 0). Ada beberapa xs, Anda menggunakan parameter untuk memberi tahu yang mana yang Anda inginkan. Anda mengulang semua peristiwa dengan forloop Anda , jadi gunakan nomor itu sebagai peristiwa saat ini yang Anda minati. Lihat saran perubahan kode saya.
MichaelHouse
0

Saya pernah mengalami masalah serupa pada Huawei U8150. Multitouch dua jari pada perangkat itu benar-benar buruk, menggunakan aplikasi tes multitouch (mungkin itu adalah "Phone Tester" tapi saya tidak yakin) Saya bisa melihat bahwa menyentuh dengan jari kedua memindahkan tempat sentuhan pertama dari banyak piksel . Jika itu masalah Anda daripada yang terkait dengan perangkat keras dan saya pikir Anda tidak bisa berbuat banyak untuk itu :(

Maaf untuk bahasa Inggris saya yang buruk

Marco Martinelli
sumber
itu bukan masalah
Green_qaue
ok, hanya sebuah pemikiran
Marco Martinelli
0

Saya percaya masalah Anda adalah bahwa Anda mengasumsikan bahwa, di antara pembaruan, urutan penunjuk diatur tetap sama. Kemungkinan besar itu tidak akan terjadi.

Bayangkan sebuah situasi di mana Anda menyentuh dengan jari A. Akan ada pointerCount () dari 1, dan A akan menjadi satu-satunya elemen yang bisa Anda tanyakan. Jika Anda menambahkan jari kedua, pointerCount () akan menjadi 2, A akan berada di indeks 0, B akan berada di indeks 1. Jika Anda menaikkan jari A, pointerCount () akan menjadi 1 lagi, dan B akan berada di indeks 0 . Jika Anda menyentuh dengan jari A lagi, A akan berada di indeks 1 dan B akan berada di indeks 0 .

Inilah sebabnya mengapa ID penunjuk disediakan, sehingga Anda dapat melacak setiap sentuhan di antara pembaruan. Jadi, jika sentuhan pertama jari B diberikan ID 12, ia akan selalu memiliki ID itu, bahkan ketika jari A dihapus dan ditambahkan kembali.

Jadi, jika kode Anda mengidentifikasi sentuhan di dekat joystick pemotretan Anda, ia harus memeriksa untuk melihat apakah sudah ada sentuhan 'pemotretan' yang sedang berlangsung. Jika tidak, maka ID dari sentuhan pemotretan harus diingat dalam beberapa variabel anggota yang tetap ada untuk pembaruan berikutnya. Di pembaruan berikutnya, jika Anda memiliki sentuhan 'pemotretan' sedang berlangsung, beralih melalui pointer dan cari pointer dengan ID yang tepat, dan itu pointer yang perlu Anda gunakan untuk melacak pembaruan, dan semua yang lain dapat diabaikan. Sama untuk sentuhan gerakan. Saat sentuhan dilepaskan, Anda menghapus ID yang terkait dengan joystick itu sehingga sentuhan baru dapat mengendalikannya.

Sekalipun sentuhan yang dimulai di dekat satu joystick jauh dari posisi sentuh semula, dengan ID-nya Anda masih dapat mengidentifikasi joystick apa yang dikontrolnya. Dan sebagai efek samping yang bagus, jari tersesat kedua di dekat joystick tertentu tidak akan memiliki efek apa pun, karena joystick akan terikat pada jari pertama yang memicu itu sampai dilepaskan.

MrCranky
sumber