올바른 멀티 터치 ID 반환


9

튜토리얼을 읽고 여기에서 multiTouch 및 Stackoverflow와 관련된 모든 질문을 보는 데 수많은 시간을 보냈습니다. 그러나 나는 이것을 올바르게하는 법을 알 수 없습니다. 나는 루프를 사용하여 내 것을 얻는다. pointerId많은 사람들 이이 일을 보지 못하지만 다소 효과가있는 유일한 방법이다.

화면에 두 개의 조이스틱이 있는데, 하나는 움직이기위한 것이고 다른 하나는 Monster Shooter에서와 같이 스프라이트 회전과 그가 촬영하는 각도를 제어하기위한 것입니다. 둘 다 잘 작동합니다.

내 문제는 내가 임 촬영과 동시에 내 스프라이트를 이동하면 내이다 touchingPoint나의 운동이 설정되어 touchingPoint때문에, 내 촬영 x하고 y온 더 높다 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);

내가 잘못하고있는 것을 절대적으로 잃어 버리지 않으면이 많은 코드를 게시하지 않을 것입니다. 멀티 터칭이 어떻게 작동하는지 잘 이해하지 못합니다.

movingPoint첫 번째 손가락과 두 번째 손가락 모두 기본적으로 변경됩니다. 나는 그것을 상자에 묶지 만,이 상자 안에 한 손가락을 대고있는 한, 두 번째 손가락이 닿는 위치에 따라 그 값이 바뀝니다. 그것은 올바른 방향으로 움직이고 아무것도 오류를주지 않습니다. 문제는 속도 변화이며 거의 두 개의 터치 포인트를 추가하는 것과 같습니다.

답변:


4

나는 이것이 당신을 위해 일할 것이라고 생각합니다.

한 가지 실수는 모든 이벤트에 대한 모든 포인터를 반복하는 것입니다. 이동 이벤트에만 필요합니다.

둘째, 실제로 인덱스 값을 getX 및 getY 함수에 넣어야하지만 게임 개체에 대한 참조로 사용할 해당 인덱스와 관련된 ID를 가져옵니다. Down 이벤트 동안 조이스틱에 ID를 할당 한 다음 포인터 색인을 반복 할 때 인덱스가 Down 이벤트 동안 조이스틱에 할당 한 포인터 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

거의 올바르게하고 있지만 포인터 ID를 사용하여 대신 X / Y를 요청해야합니다. i

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

MotionEvent 설명서에서 :

모션 이벤트 내에서 개별 포인터가 나타나는 순서는 정의되어 있지 않습니다. 따라서 포인터의 포인터 인덱스는 한 이벤트에서 다음 이벤트로 변경 될 수 있지만 포인터의 포인터 ID는 포인터가 활성 상태를 유지하는 한 일정하게 유지됩니다. getPointerId (int) 메소드를 사용하여 제스처의 모든 후속 모션 이벤트에서 포인터를 추적 할 포인터의 포인터 ID를 확보하십시오. 그런 다음 연속적인 모션 이벤트의 경우 findPointerIndex (int) 메서드를 사용하여 해당 모션 이벤트에서 지정된 포인터 ID에 대한 포인터 인덱스를 가져옵니다.

event.getX/Yi보장 순서가 없기 때문에 포인터 ID가 아닌 포인터 ID가 필요합니다 .

또한 미묘하지만 중요한 또 다른 문제가 있습니다. getAction () 함수 패밀리가 매개 변수를 사용하지 않는 방법에 주목하십시오. 좀 이상해? X / Y를 얻으려면 포인터 ID가 필요하지만 동작이 수행되지 않습니까? 그것은 몇 가지 중요한 것들을 암시합니다.

  • 각 포인터의 각 동작에 대해 터치 핸들러를 호출하고 모든 포인터에 대해 프레임 당 단일 호출이 아닙니다.
  • getX / Y는 지금까지 포인터 추적을보고 최신 값을 리턴하지만 getAction은 현재 이벤트에 대해서만 문의합니다.

따라서 포인터 동작 (아래 / 이동 / 위) 마다 터치 핸들러를 한 번 호출 할 수 있습니다 . 따라서 두 손가락 이동 = 2 호출. 코드의 부작용은 하나의 이벤트 동작을 모든 포인터에 적용한다는 것입니다.

따라서 추적을 반복하는 대신 현재 이벤트에 대해서만 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

또한이 줄을 사용할 때 : int pid = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;eclipse는 supressWarning을 추가하도록 지시합니다. 정상입니까? 자세한 답변 후 모든 질문에 대해 죄송합니다. MotionEvent는 저에게 아주 새로운 것이며 어떤 이유로 논리를 이해할 수 없습니다
Green_qaue

당신이 볼 수 있듯이 업데이트 된 코드가 추가되어 실제로 무엇을 해야할지 모르겠 pid으며 루프는 여전히 존재 i합니다.
Green_qaue

@Max : sendTouchToGameEngine은이 이벤트를 내 게임 엔진 대기열에 추가하여 다음 업데이트 중에 처리 할 호출입니다. 큐를 사용하여 입력 이벤트를 폴링하지 않으면 TouchEvent가 UI 스레드 및 아마도 게임 엔진 업데이트에서 제공되므로 호출 함수가 예기치 않은 방식으로 게임 엔진 상태를 망칠 위험이 있습니다. 다른 스레드에서 실행
ADB

@ 맥스 : 불행히도 경고에 대해 모르겠습니다. 어느 쪽인지 말씀해 주시겠습니까?
ADB

0

이 코드에는 약간의 이상한 점이 있습니다. 나는 안드로이드를 사용하지 않으므로 아마도 이것이 아닌 것이라고 생각합니다. 그러나 나는 두 가지 다른 방식으로 두 가지 입장을 취하고 있음을 알았습니다.

먼저 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

다운 투표하면 동의합니다. 이유를 알려주십시오. 당신은 이전 방법에서 옳습니다. 나는 약 백만 가지를 시도했지만 그것을 제거하는 것을 잊었습니다.
Green_qaue

질문에 코드를 업데이트하여 반영 할 수 있습니까? x및 의 설정을 제거 y했지만 id나중에 여전히 위치를 파악하고 있습니다.
MichaelHouse

아, 대답을 이해하는 데 시간이 좀 걸렸습니다. 그러나 이것은 어떻게 작동합니까? 동일한 변수 ( x=event.getX(i))를 사용하면 1 이상일 x때마다 2 개의 값을 저장 pointerCount해야합니까? 그리고 그 느낌은 옳습니다.
Green_qaue

1
내가 이해 한 event것은 실제로 이벤트 목록입니다. 당신이하고있는 것처럼 목록을 통해 다른 이벤트에 액세스 할 수 있습니다. 따라서 x두 번째 이벤트 의 위치에 액세스 하려면 event.getX(1)(0에서 시작하기 때문에) 여러 개가 x있으며 매개 변수를 사용하여 원하는 것을 알려줍니다. 루프로 모든 이벤트를 for반복하고 있으므로 해당 숫자를 관심있는 현재 이벤트로 사용하십시오. 내 코드 변경 제안을 참조하십시오.
MichaelHouse

0

Huawei U8150에서도 비슷한 문제가 발생했습니다. 해당 장치의 두 손가락 멀티 터치가 멀티 터치 테스트 앱을 사용하여 실제로 나빴습니다 (아마 "폰 테스터"였지만 확실하지 않습니다). 두 번째 손가락으로 터치하면 많은 픽셀의 첫 번째 터치 지점이 움직 인 것을 볼 수있었습니다 . 그것이 하드웨어와 관련된 것보다 당신의 문제라면 나는 그것을 위해 많은 것을 할 수 있다고 생각하지 않습니다 :(

내 하찮은 영어 실력에 죄송하다는 말씀을 드리고 싶습니다


그것은 문제가 아닙니다
Green_qaue

좋아, 그냥 생각했다
Marco Martinelli

0

귀하의 문제는 업데이트 사이에 포인터 배열 순서가 동일하게 유지된다고 가정하고 있다는 것입니다. 아마도 그렇지 않을 것입니다.

손가락 A로 터치하는 상황을 상상해보십시오. 1의 pointerCount ()가 있고 A가 요청할 수있는 유일한 요소가됩니다. 두 번째 손가락을 추가하면 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를 통해 제어하는 ​​조이스틱을 정확하게 식별 할 수 있습니다. 그리고 좋은 부작용으로, 특정 조이스틱 근처의 두 번째 길 잃은 손가락은 아무런 영향을 미치지 않습니다. 조이스틱은 손을 until 때까지 트리거 한 첫 번째 손가락에 바인딩되기 때문입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.