trả về id multiTouch chính xác


9

Tôi đã dành vô số giờ để đọc hướng dẫn và xem xét mọi câu hỏi liên quan đến multiTouch từ đây và Stackoverflow. Nhưng tôi không thể tìm ra cách để làm điều này một cách chính xác. Tôi sử dụng một vòng lặp để có được cái của mình pointerId, tôi không thấy nhiều người làm việc này nhưng đó là cách duy nhất tôi quản lý để làm cho nó hoạt động được phần nào.

Tôi có hai cần điều khiển trên màn hình, một để di chuyển và một để điều khiển xoay vòng xoay của tôi và góc anh ta bắn, giống như trong Monster Shooter. Cả hai đều hoạt động tốt.

Vấn đề của tôi là khi tôi Di chuyển sprite của mình cùng lúc với Im chụp, touchingPointchuyển động của tôi được đặt ở chế touchingPointđộ chụp của tôi, vì xycao hơn ở lần touchingPointchụp của tôi ( moving-stickở bên trái màn hình, shooting-stickở bên phải) , sprite của tôi tăng tốc, điều này tạo ra một sự thay đổi không mong muốn về tốc độ cho sprite của tôi.

đây là cách tôi giải quyết nó với sự giúp đỡ của bạn đây là cho bất cứ ai có thể gặp phải một vấn đề tương tự:

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

Sẽ không đăng nhiều mã này nếu tôi không mất hoàn toàn những gì tôi đang làm sai. Tôi chỉ đơn giản là không thể hiểu rõ về cách hoạt động của MultiTouching.

movingPointthay đổi cơ bản cho cả ngón tay thứ nhất và thứ hai của tôi. Tôi liên kết nó với một hộp, nhưng khi tôi giữ một ngón tay trong hộp này, nó sẽ thay đổi giá trị của nó dựa trên nơi ngón tay thứ hai của tôi chạm vào. Nó di chuyển đúng hướng và không có gì sai sót, vấn đề là sự thay đổi tốc độ, nó gần giống như nó cộng hai điểm chạm.

Câu trả lời:


4

Tôi nghĩ rằng điều này sẽ làm việc cho bạn.

Một sai lầm bạn đã làm là lặp đi lặp lại trên tất cả các con trỏ cho mọi sự kiện. Nó chỉ cần thiết cho các sự kiện di chuyển.

Thứ hai, bạn thực sự cần phải đặt giá trị chỉ mục vào các hàm getX và getY, nhưng bạn lấy id được liên kết với chỉ mục đó để sử dụng làm tham chiếu đến các đối tượng trò chơi của bạn. Bạn gán id cho phím điều khiển trong Sự kiện Xuống và sau đó khi lặp qua các chỉ mục con trỏ, hãy kiểm tra xem chỉ mục có được liên kết với id con trỏ mà bạn đã gán cho phím điều khiển trong sự kiện Xuống không. Nếu có, hãy kiểm tra xem nó có còn trong giới hạn không và cập nhật hoặc vô hiệu hóa nó.

Tôi không kiểm tra mã này, nhưng tôi biết nó hoạt động theo khái niệm vì tôi sử dụng phương thức trong mã của riêng tôi. Hãy cho tôi biết nếu có bất kỳ vấn đề nào với nó mà bạn không thể tìm ra.

Đầu tiên, bạn sẽ phải thêm các mục sau vào lớp cần điều khiển của mình.

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

Thay đổi onTouchEvent của bạn thành này.

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

Ngài là người hùng của tôi. Tôi sẽ thêm mã cập nhật vào câu hỏi của tôi để bạn có thể thấy tôi đã giải quyết nó như thế nào với sự giúp đỡ của bạn! bây giờ tôi cuối cùng cũng có thể kết thúc trò chơi của mình: D
Green_qaue

7

Bạn gần như đã hiểu đúng, nhưng bạn nên sử dụng id con trỏ để yêu cầu X / Y thay vì i

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

Từ tài liệu MotionEvent:

Thứ tự mà các con trỏ riêng lẻ xuất hiện trong một sự kiện chuyển động không được xác định. Do đó, chỉ mục con trỏ của một con trỏ có thể thay đổi từ sự kiện này sang sự kiện tiếp theo nhưng id con trỏ của con trỏ được đảm bảo không đổi miễn là con trỏ vẫn hoạt động. Sử dụng phương thức getPulumId (int) để lấy id con trỏ của con trỏ để theo dõi nó trên tất cả các sự kiện chuyển động tiếp theo trong một cử chỉ. Sau đó, đối với các sự kiện chuyển động liên tiếp, hãy sử dụng phương thức findPulumIndex (int) để lấy chỉ mục con trỏ cho id con trỏ đã cho trong sự kiện chuyển động đó.

event.getX/Yyêu cầu một id con trỏ, không i, bởi vì không có người bảo đảm họ sẽ theo cùng một thứ tự.

Ngoài ra, có một vấn đề tinh tế nhưng quan trọng khác. Lưu ý cách họ hàm getAction () không lấy tham số. Điều đó thật kỳ lạ đúng không? Bắt X / Y yêu cầu id con trỏ, nhưng không thực hiện hành động? Đó là gợi ý về một vài điều quan trọng:

  • bạn nhận được một cuộc gọi đến bạn chạm vào trình xử lý cho từng hành động của từng con trỏ và không phải là một cuộc gọi trên mỗi khung cho tất cả các con trỏ
  • getX / Y nhìn vào dấu vết của con trỏ cho đến nay và trả về giá trị mới nhất, trong khi getAction chỉ hỏi về sự kiện hiện tại

Vì vậy, điều đó có nghĩa là bạn nhận được một cuộc gọi đến trình xử lý cảm ứng cho mỗi hành động con trỏ (xuống / di chuyển / lên). Vậy hai ngón tay di chuyển = 2 cuộc gọi. Một tác dụng phụ khó chịu của mã của bạn là nó áp dụng hành động của một sự kiện cho tất cả các con trỏ ...

Vì vậy, thay vì lặp lại các dấu vết, chỉ cần lấy pid / x / y / hành động cho sự kiện hiện tại (đối với chuyển động đồng thời, bạn sẽ nhận được một cuộc gọi khác đến trình xử lý của mình một lát sau, như tôi đã nói)

Đây là mã của tôi để xử lý các sự kiện:

 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;

}

Để quay lại mã của bạn, bạn sẽ có thể đơn giản hóa nó theo cách sau. Vòng lặp đã biến mất, nếu không, nếu bạn có các điều khoản khác mà bạn không đề cập, bạn luôn có thể thêm lại. Nó hoạt động bằng cách theo dõi id con trỏ nào được sử dụng cho điều khiển (di chuyển / bắn) khi DOWN được kích hoạt và đặt lại chúng khi LÊN. Vì bạn không sử dụng cử chỉ, bạn có thể giới hạn công tắc () của mình thành XUỐNG, LÊN, DI CHUYỂN và BÊN NGOÀI.

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

    }
}

cảm ơn vì câu trả lời tuyệt vời này, thực sự làm sáng tỏ một vài điều. Bạn có thể giải thích những gì dòng sendTouchToGameEngine(pid, actionCode, (int)event.getX(pid), (int)event.getY(pid));này làm và khi bạn gọi nó?
Green_qaue

Ngoài ra khi tôi sử dụng dòng này: int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;nhật thực bảo tôi thêm supressWarning, điều đó có bình thường không? Xin lỗi cho tất cả các câu hỏi sau khi một câu trả lời chi tiết như vậy. MotionEvent rất mới đối với tôi và không thể hiểu logic vì một số lý do
Green_qaue

đã thêm mã cập nhật, như bạn có thể thấy tôi không thực sự biết phải làm gì pidvà vòng lặp vẫn còn đó, sẽ không hoạt động nếu không có nó, vì tôi cần phải lấy i.
Green_qaue

@Max: sendTouchToGameEngine chỉ đơn giản là một cuộc gọi để thêm sự kiện này vào hàng đợi công cụ trò chơi của tôi để được xử lý trong lần cập nhật tiếp theo. Nếu bạn không sử dụng hàng đợi để thăm dò sự kiện đầu vào của mình, bạn sẽ gặp rủi ro về chức năng gọi điện của mình gây rối với trạng thái của công cụ trò chơi theo những cách không mong muốn, vì TouchEvent đến từ luồng UI và, có lẽ là cập nhật công cụ trò chơi của bạn chạy trong một chủ đề khác
ADB

@Max: Tôi không biết về cảnh báo không may. Bạn có thể cho chúng tôi biết đó là cái gì không?
ADB

0

Có một chút kỳ lạ xảy ra với mã này. Tôi không sử dụng Android, vì vậy có lẽ tôi nghĩ đây không phải là thứ gì đó. Tuy nhiên tôi đã nhận thấy rằng bạn đang nhận được vị trí hai lần, theo hai cách khác nhau:

Đầu tiên bạn có được nó như thế này khi bắt đầu pointerCountvòng lặp của bạn :

(int) event.getX(i)

Sau đó, bên trong câu lệnh chuyển đổi của bạn, bạn nhận được nó như vậy:

(int) event.getX(id)

Lưu ý rằng bạn chuyển từ sử dụng isang sử dụng id.

Tôi cho rằng nó được coi là phương pháp cũ. Tôi khuyên bạn nên thay thế tất cả các trường hợp (int) event.getX(id)và sử dụng giá trị xbạn đặt lúc đầu. Tương tự như vậy (int) event.getY(id)để trao đổi y.

Hãy thử hoán đổi những phần này:

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

Sau đó, bên trong chuyển đổi của bạn sử dụng này:

    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
Muốn bình luận về lý do tại sao điều này không hữu ích và xứng đáng với một downvote? Đó là điều lịch sự phải làm.
MichaelHouse

Đồng ý, nếu bạn bỏ phiếu. Hãy cho anh ấy biết tại sao. Bạn đúng từ một phương pháp trước đây. Tôi đã thử khoảng một triệu thứ khác nhau vì vậy tôi chỉ quên loại bỏ nó.
Green_qaue

Bạn có thể cập nhật mã trong câu hỏi của bạn để phản ánh điều đó? Tôi thấy bạn đã xóa cài đặt của xy, nhưng bạn vẫn nhận được vị trí idsau đó.
MichaelHouse

aah, tôi mất một lúc để hiểu câu trả lời của bạn Nhưng làm thế nào điều này sẽ làm việc? Nếu tôi sử dụng cùng một biến ( x=event.getX(i)), thì xsẽ phải lưu 2 giá trị bất cứ khi nào pointerCountnhiều hơn 1, đúng không? Và dosnt cảm thấy đúng.
Green_qaue

1
Từ những gì tôi hiểu eventthực sự là một danh sách các sự kiện. Bạn truy cập các sự kiện khác nhau bằng cách đi qua danh sách như bạn đang làm. Vì vậy, để truy cập xvị trí cho sự kiện thứ hai bạn sử dụng event.getX(1)(vì chúng tôi bắt đầu từ 0). Có nhiều xs, bạn đang sử dụng tham số để nói cho bạn biết bạn muốn gì. Bạn đang lặp qua tất cả các sự kiện với forvòng lặp của mình , vì vậy hãy sử dụng số đó làm sự kiện hiện tại mà bạn quan tâm. Xem đề xuất thay đổi mã của tôi.
MichaelHouse

0

Tôi đã gặp vấn đề tương tự một lần trên Huawei U8150. Cảm ứng đa điểm hai ngón tay trên thiết bị đó thực sự rất tệ, sử dụng ứng dụng kiểm tra đa điểm (có thể là "Trình kiểm tra điện thoại" nhưng tôi không chắc) Tôi có thể thấy rằng việc chạm bằng ngón tay thứ hai đã di chuyển điểm chạm thứ nhất của nhiều pixel . Nếu đó là vấn đề của bạn hơn là liên quan đến phần cứng và tôi không nghĩ bạn có thể làm được gì nhiều cho nó :(

Xin lỗi vì tiếng Anh của tôi không tốt


đó không phải là vấn đề
Green_qaue

ok, chỉ là một ý nghĩ
Marco Martinelli

0

Tôi tin rằng vấn đề của bạn là bạn đang giả định rằng, giữa các bản cập nhật, thứ tự các con trỏ được sắp xếp giữ nguyên. Điều đó rất có thể sẽ không xảy ra.

Tưởng tượng một tình huống mà bạn chạm bằng ngón tay A. Sẽ có một con trỏCount () là 1 và A sẽ là yếu tố duy nhất bạn có thể hỏi về. Nếu bạn thêm ngón tay thứ hai, con trỏCount () sẽ là 2, A sẽ ở chỉ số 0, B sẽ ở chỉ số 1. Nếu bạn nâng ngón tay A, con trỏCount () sẽ lại là 1 và B sẽ ở chỉ số 0 . Nếu sau đó bạn chạm lại bằng ngón tay A, A sẽ ở chỉ số 1 và B sẽ ở chỉ số 0 .

Đây là lý do tại sao ID con trỏ được cung cấp, để bạn có thể theo dõi các lần chạm riêng lẻ giữa các bản cập nhật. Vì vậy, nếu lần chạm đầu tiên của ngón tay B được gán ID 12, nó sẽ luôn có ID đó, ngay cả khi ngón tay A được gỡ bỏ và thêm lại.

Vì vậy, nếu mã của bạn xác định một lần chạm gần phím điều khiển chụp của bạn, nó phải kiểm tra xem liệu đã có một lần chạm 'chụp' nào chưa. Nếu không, thì nên nhớ ID của cảm ứng chụp trong một số biến thành viên vẫn tiếp tục cập nhật tiếp theo. Trong các bản cập nhật tiếp theo, nếu bạn có tiến trình 'chụp', hãy lặp qua các con trỏ và tìm con trỏ với ID đúng, và đó là con trỏ bạn cần sử dụng để theo dõi các cập nhật và có thể bỏ qua tất cả các con trỏ khác. Tương tự cho cảm ứng chuyển động. Khi cảm ứng được giải phóng, bạn xóa ID được liên kết với phím điều khiển đó để một lần chạm mới có thể kiểm soát nó.

Ngay cả khi một cú chạm bắt đầu gần một phím điều khiển đi xa khỏi vị trí chạm ban đầu, bằng ID của nó, bạn vẫn có thể xác định chính xác phím điều khiển mà nó đang điều khiển. Và như một hiệu ứng phụ tuyệt vời, một ngón tay đi lạc thứ hai gần một phím điều khiển cụ thể sẽ không có tác dụng gì, bởi vì cần điều khiển sẽ bị ràng buộc với ngón tay thứ nhất đã kích hoạt nó cho đến khi nó được phát hành.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.