0

经过大量的修修补补,我想我终于为我的安卓游戏想出了一个好的多点触控处理系统。它利用了 Robert Greene 的 Input Pipeline,经过修改后可与多点触控一起使用。现在,代码有一个简单的系统,可以记录哪个指针 ID 当前正在执行哪个动作(现在只是射击和移动)。每个指针的状态都保存在一个Pointer类中,它只是简单的封装了它是否向下,以及它的坐标。ID 充当指针数组索引。

这似乎在实践中应该运作良好,但在游戏中它的行为非常不稳定。在 LogCat 中记录指针动作时,Android 经常会在指针保持向下时发送“UP”动作,或者就在指针的一些“MOVE”动作之前发送一个“UP”动作。因为我的代码认为指针是向上的,所以游戏没有响应它。

这也发生在按下按钮(如拍摄按钮)时。当指针落在该区域(现在只是左下区域)时,Android 将发送多个“向上”和“向下”动作,即使指针始终保持向下。我之前有一个单点触控移动系统,这些问题都没有发生。

这只是我对事件的反应方式的问题吗?我应该分别处理 POINTER_DOWN 和 DOWN 吗?或者我应该在“UP”操作之后检测哪些指针正在移动,以查看哪些指针确实下降了,尽管 Android 说了什么?

这是我线程中的当前代码,它接收来自 Android 的输入事件。因为它是一个管道系统,所以我将事件封装在 InputObject 中,这有点类似于 Robert Greene 的。也许一双新的眼睛可以帮助我分辨出什么问题?谢谢你的帮助!

private int inx, iny;
private int shootID;
public boolean shooting = false;
private int moveID;
public boolean moveDown = false;

private static final int MAX_POINTERS = 10;
private Pointer[] pointers = new Pointer[MAX_POINTERS];

public void inputTouch(InputObject in) {

    switch(in.action) {
    case MotionEvent.ACTION_DOWN:
    case MotionEvent.ACTION_POINTER_DOWN:
        pointers[in.pID].press(in.pX[in.actionIndex], in.pY[in.actionIndex]);
        //Log.i("D", "DOWN");
        break;
    case MotionEvent.ACTION_MOVE:
        for(int p = 0; p < in.pointCount; p++) {
            int id = in.pointerIDs[p];
            pointers[id].setCoord(in.pX[id], in.pY[id]);
        }
        //Log.i("D", "MOVE");
        break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_POINTER_UP:
        pointers[in.pID].release();

        if(shootID == in.pID) {
            shooting = false;
        }

        if(moveID == in.pID) {
            moveDown = false;
        }

        //Log.i("D", "UP");
        break;
    case MotionEvent.ACTION_CANCEL:
    default:
        break;
    }

    for(int ap = 0; ap < MAX_POINTERS; ap++) {
        if(pointers[ap].down) {
            if(pointers[ap].x < world.cam.pixelWidth / 4 &&
                    pointers[ap].y > world.cam.pixelHeight - (world.cam.pixelHeight / 4)) {

                shootID = ap;
                shooting = true;
            } else {
                inx = pointers[ap].x;
                iny = pointers[ap].y;
                moveID = ap;
                moveDown = true;
            }
        }
    }

    StringBuilder sb = new StringBuilder();
    for(int j = 0; j < 3; j++) {
        sb.append("ID " + (j+1) + ": " + pointers[j].down + "[" + pointers[j].x + ", " + pointers[j].y + "]" + " | ");
    }
    //Log.i("D", sb.toString());
}
4

1 回答 1

0

希望您现在得到答案,但这是我的解决方案:

首先,要在 InputHolder 类中添加一些内容:

- mPointerIndex = event.getPointerIndex 和 mPointerID = event.getPointerID(mPointerIndex) 的内部公共字段(这些在 useEvent/useHistory 中分配)

- 如果只需要跟踪 2 个接触点,则还需要添加 mPointerIndex2、x2 和 y2。根据需要添加更多以跟踪更多点。

-为 MotionEvent.ACTION_POINTER_2_UP/DOWN 被传递的情况添加定义。事实证明,POINTER_1 与 POINTER_UP/DOWN 相同!这真的让我大吃一惊,因为我正在通过开关盒进入默认设置。我只是抓住了这一点,因为我将默认值更改为 -1 并看到它已记录。根据您在 processInput(obj) 中处理操作的方式,您可以将它们映射到不同的整数。在我的情况下,我使用 obj.y 来查看它们是否在左/右触摸位置,我只需要两个点,所以我将它们映射到 ACTION_TOUCH_POINTER_UP/DOWN 而不是给每个触摸它自己的动作 int 标识符。

- 现在,如果您想跟踪多个接触点,您必须在 for 循环中对 event.getPointerCount() 中的所有条目执行上述和以下操作。在我的情况下,我只对另一个接触点的 x/y 感兴趣,所以我可以在填写第一个点之后只进行检查,并且很容易推断出另一个指针索引:

public void useEvent(MotionEvent event) {
    eventType = EVENT_TYPE_TOUCH;
    int a = event.getAction();
    switch (a) {
        case MotionEvent.ACTION_DOWN:
        action = ACTION_TOUCH_DOWN;
        break;
        case MotionEvent.ACTION_POINTER_DOWN:
        action = ACTION_TOUCH_POINTER_DOWN;
        break;
        case MotionEvent.ACTION_POINTER_2_DOWN:
        action = ACTION_TOUCH_POINTER_DOWN;
        break;
        case MotionEvent.ACTION_MOVE:
        action = ACTION_TOUCH_MOVE;
        break;
        case MotionEvent.ACTION_UP:
        action = ACTION_TOUCH_UP;
        break;
        case MotionEvent.ACTION_POINTER_UP:
        action = ACTION_TOUCH_POINTER_UP;
        break;
        case MotionEvent.ACTION_POINTER_2_UP:
        action = ACTION_TOUCH_POINTER_UP;
        break;
        default:
        action = -1;
    }
    time = event.getEventTime();

    pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
    pointerID = event.getPointerId(pointerIndex);

    x = (int) event.getX(pointerIndex);
    y = (int) event.getY(pointerIndex);

    if (event.getPointerCount() > 1)
    {
        pointerIndex2 = pointerIndex== 0 ? 1 : 0;
        x2 = (int)event.getX(pointerIndex2);
        y2 = (int)event.getY(pointerIndex2);
    }
}

如果您要跟踪更多点,或者需要有关每个触摸事件的更多信息,则必须将 for 循环向上扩展至动作开关案例。

无论如何,现在您的线程中需要两个变量,这样您就可以跟踪您的两次触摸:调用它们 touchOneID 和 touchTwoID(或索引)。在 ACTION_POINTER_2_UP 上,obj.mPointerID 指的是正在上升的 obj!这意味着另一个触摸会改变它的ID!跟踪此更改,然后您就可以排序了。内部 obj 的 mPointerID/Index 将始终正确,您只需在 Surfaceview 的线程中正确跟踪它们,以便您可以在收到 POINTER_DOWN 时采取相应措施。就我而言,我做了一个简单的 x 位置检查以确定在我的 ACTION_MOVE 事件中要做什么,这足以告诉我正确地确定我应该使用哪个 x/y 或 x2/y2 以及如何使用它。这意味着要运行的代码更少,并且限制了我必须保留在内存中的内容,但这完全取决于您需要什么以及需要多少信息。

最后:老实说,如果您正确处理了定义并分配了每个 MotionEvent 并将它们保存在 InputObject 中,那么您可能会没事的。见鬼,我认为您可以忽略并丢失整个开关盒,只需说 obj.mAction = event.getAction() 并在您的 processInput(obj) 中处理这些!意思是,他在 InputHolder 中重新映射到的所有静态整数似乎都是不必要的,除非你真的只需要一两个触摸定义(这解释了他神秘的独立评论“这个应用程序只对向下触摸感兴趣”)。摆脱那些静态定义的整数也意味着您可以只针对 MotionEvent.ACTION_CODE 进行测试,而不是针对 InputHolder.TOUCH_ACTION_CODE 进行查找。

于 2013-11-11T19:30:15.280 回答