0

我正在做一个简单的三消游戏,类似于宝石迷阵,我只想通过触摸精灵对象来移动精灵对象,然后在左、右、上、下等四个方向上移动一步。为此,我将 Down 上的 X 和 Y 值与 Move 上的 X 和 Y 值进行比较。它有效,但远非完美!如果运动不直,很容易得到错误的值。我的问题是:有没有办法改进它并使它变得更好?

我也看过手势,但这似乎非常复杂,与我拥有的表面视图一起使用。

@Override
public boolean onTouch(View v, MotionEvent event) {

    switch (event.getAction()) {

    case MotionEvent.ACTION_DOWN:
        Log.i("test","Down");

        touchActionDownX = (int)event.getX();
        touchActionDownY = (int)event.getY();
        touchActionMoveStatus = true;

        gameLoop.touchX = (int)event.getX();
        gameLoop.touchY = (int)event.getY();
        gameLoop.touchActionDown = true;
        break;

    case MotionEvent.ACTION_POINTER_UP:

        touchActionMoveStatus = true;

        break;

    case MotionEvent.ACTION_MOVE:
        //Log.i("test","Move");
        gameLoop.touchActionMove = true;

        if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        if(touchActionMoveX < touchActionDownX)
            Log.i("test","Move Left");
        else if(touchActionMoveX > touchActionDownX)
            Log.i("test","Move Right");
        else if(touchActionMoveY < touchActionDownY)
            Log.i("test","Move Up");
        else if(touchActionMoveY > touchActionDownY)
            Log.i("test","Move Down");

        touchActionMoveStatus = false; // Will be set to true when pointer is up
        }

        break;
    }

    // return false;
    return true; // This gets the coordinates all the time
}
4

3 回答 3

3

我会选择移动最大的维度完全忽略另一个,例如,如果移动是 x=10 且 y=8,则只使用 x 维度(即左/右),反之亦然。

同样正如Larry McKenzie所指出的,使用阈值忽略较小的移动是防止记录用户无意的意外移动的好主意。将阈值调整为感觉自然的值。

这是使用您的示例的一些代码(仅 ACTION_MOVE 案例):

case MotionEvent.ACTION_MOVE:
    //Log.i("test","Move");
    gameLoop.touchActionMove = true;

    if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        // setup a threshold (below which no movement would occur)
        int threshold = 5;        /* tweak this as needed */ 

        // first calculate the "delta" movement amounts
        int xMove = touchActionMoveX - touchActionDownX;
        int yMove = touchActionMoveY - touchActionDownY;

        // now find the largest of the two (note that if they
        // are equal, x is assumed largest)
        if ( Math.abs( xMove ) >= Math.abs( yMove ) )  {  /* X-Axis */
           if ( xMove >= threshold )
              Log.i("test","Move Right");
           else if ( xMove <= -threshold )
              Log.i("test","Move Left");
        }
        else  {                                          /* Y-Axis */
           if ( yMove >= threshold )
              Log.i("test","Move Down");
           else if ( yMove <= -threshold )
              Log.i("test","Move Up");
        }

        touchActionMoveStatus = false; // Will be set to true when pointer is up
    }
}
break;

注意:正如在其他一些答案中所提到的,因为可能会发生具有非常小的值的多个事件,所以最好累积(即总结)移动直到达到阈值 - 您可以使用在 ACTION_DOWN 中重置的成员. 一旦达到阈值(在任一维度上),您就可以检查哪个方向。

替代方法

另一种解决方法是检测FIRST ACTION_MOVE 事件中的最大运动,然后将所有进一步的运动锁定到该维度。为此,您需要添加各种状态成员 - 这些需要在每个状态中更新。这是一个粗略的例子(只有状态跟踪):

  // members
  private boolean axisLock = false;    /* Track When Lock is Required */
  private boolean axisX = true;        /* Axis to Lock (true) for X, (false) for Y */


  @Override
  public boolean onTouch(View v, MotionEvent event) {

      switch (event.getAction()) {

      case MotionEvent.ACTION_DOWN:

          // set this state so that ACTION_MOVE knows a lock is required
          axisLock = true;

          break;

      case MotionEvent.ACTION_UP:

          // clear the state in case no move was made
          axisLock = false;

      case MotionEvent.ACTION_MOVE:

          // now lock the axis if this is the first move event
          if ( axisLock )  {

             // this will set whether the locked axis is X (true) or Y (false)
             axisX = event.getX() >= event.getY();

             // reset the state (to keep the axis locked)
             axisLock = false;
          }

          // at this point you only need to consider the movement for the locked axis
          if ( axisX )  {
             int movement = (int)event.getX();    /* Get Movement for Locked Axis */
             // check for your movement conditions here
          }
          else  {
             int movement = (int)event.getY();    /* Get Movement for Locked Axis */
             // check for your movement conditions here
          }

          break;
      }

      return true;
  }

您可以向此代码添加许多优化,现在它只是说明了基本思想。

于 2013-05-25T08:42:38.487 回答
3

尝试这样的事情:

@Override
public boolean onTouch(View v, MotionEvent event) {

    //You may have to play with the value and make it density dependant.
    int threshold = 10;

    switch (event.getAction()) {

    case MotionEvent.ACTION_DOWN:
        Log.i("test","Down");

        touchActionDownX = (int)event.getX();
        touchActionDownY = (int)event.getY();
        touchActionMoveStatus = true;

        gameLoop.touchX = (int)event.getX();
        gameLoop.touchY = (int)event.getY();
        gameLoop.touchActionDown = true;
        break;

    case MotionEvent.ACTION_POINTER_UP:

        touchActionMoveStatus = false;

        break;

    case MotionEvent.ACTION_MOVE:
        //Log.i("test","Move");
        gameLoop.touchActionMove = true;

        if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        if(touchActionMoveX < (touchActionDownX - threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY  (touchActionDownY + threshold))){
            Log.i("test","Move Left");//If the move left was greater than the threshold and not greater than the threshold up or down
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveX > (touchActionDownX + threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY < (touchActionDownY + threshold))){
            Log.i("test","Move Right");//If the move right was greater than the threshold and not greater than the threshold up or 
            touchActionMoveStatus = false;
       }
        else if(touchActionMoveY < (touchActionDownY - threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
            Log.i("test","Move Up");//If the move up was greater than the threshold and not greater than the threshold left or right
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveY > (touchActionDownY + threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
            Log.i("test","Move Down");//If the move down was greater than the threshold and not greater than the threshold left or right
            touchActionMoveStatus = false;
        }
        }

        break;
    }

    // return false;
    return true; // This gets the coordinates all the time
}

或使用比率:

@Override
public boolean onTouch(View v, MotionEvent event) {

    //You may have to play with the value. 
    //A value of two means you require the user to move twice as 
    //far in the direction they intend to move than any perpendicular direction.
    float threshold = 2.0;

    switch (event.getAction()) {

    case MotionEvent.ACTION_DOWN:
        Log.i("test","Down");

        touchActionDownX = (int)event.getX();
        touchActionDownY = (int)event.getY();
        touchActionMoveStatus = true;

        gameLoop.touchX = (int)event.getX();
        gameLoop.touchY = (int)event.getY();
        gameLoop.touchActionDown = true;
        break;

    case MotionEvent.ACTION_POINTER_UP:

        touchActionMoveStatus = true;

        break;

    case MotionEvent.ACTION_MOVE:
        //Log.i("test","Move");
        gameLoop.touchActionMove = true;

        if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        // I haven't tested this so you may have a few typos to correct.
        float ratioLeftRight = Math.abs(touchActionMoveX - touchActionDownX)/Math.abs(touchActionMoveY - touchActionDownY)
        float ratioUpDown = Math.abs(touchActionMoveY - touchActionDownY)/Math.abs(touchActionMoveX - touchActionDownX)

        if(touchActionMoveX < touchActionDownX && ratioLeftRight > threshold){
            Log.i("test","Move Left");
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveX > touchActionDownX && ratioLeftRight > threshold){
            Log.i("test","Move Right");
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveY < touchActionDownY && ratioUpDown > threshold){
            Log.i("test","Move Up");
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveY > touchActionDownY && ratioUpDown > threshold){
            Log.i("test","Move Down");
            touchActionMoveStatus = false;
        }
        }

        break;
    }

    // return false;
    return true; // This gets the coordinates all the time
}
于 2013-05-25T07:54:43.153 回答
2

larry 的想法是对的,我只是想解决一下问题,

//put this in the wraping class  
private static int THRESHOLD = 10;
private static int initX;
private static int initY;

@Override
public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:
    initX = (int)event.getX();
    initY = (int)event.getY();
    break;

case MotionEvent.ACTION_POINTER_UP:
   //you can add in some kind of "move back" animation for the item
    break;

case MotionEvent.ACTION_MOVE:
if(((int)event.getY - initY) > THRESHOLD){
   //move down
   break;
}

if(((int)event.getY - initY) > -THRESHOLD){
   //move up
   break;
}

if(((int)event.getX - initX) > THRESHOLD){
   //move right
   break;
}

if(((int)event.getX - initX) < -THRESHOLD){
   //move left
   break;
}
break;    
}
}

我没有测试这段代码,只是免费编写它,但我希望你能明白我的想法:)

于 2013-05-25T09:03:38.933 回答