2011-03-13 58 views
3

我正在开发类似于高峰时间/交通阻塞/阻塞益智游戏的Android游戏。董事会是一个包含长方形件的广场。长件只能水平移动,而高块件只能垂直移动。目标是释放红色的棋子并将其移出棋盘。这个游戏只是我的第二个任何语言的编程项目,所以任何提示或最佳做法将与您的答案一起赞赏。drag-n-drop益智游戏的碰撞问题

我有一个名为Pieces的游戏作品,它描述了它们的大小和绘制方式,给它们拖放功能以及检测和处理碰撞。

然后我有一个名为GameView的活动类,它创建我的布局并创建Pieces对象以添加到名为Board的RelativeLayout。我曾考虑让董事会成为自己的班级,但还没有需要。

这里就是我的工作进展的样子:
screen_cap1

我的问题:
这其中大部分的作品,除了我的冲突处理完全正常。它似乎是在很好地检测到碰撞,而不是在发生碰撞时将它们推到彼此的外面,而是在它被拖拽到的位置和应该在的位置之间(似乎是)之间来回跳动。它看起来像这样:
screencapgif1
另一个奇怪的是:当拖动的块与其左边的一块相撞时,碰撞处理似乎完美地工作。只有在上面,下面和右侧出现问题。
这里的碰撞代码:

@Override 
public boolean onTouchEvent(MotionEvent event){ 

    float eventX = event.getX(); 
    float eventY = event.getY(); 

    switch (event.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
     //check if touch is on piece 
     if (eventX > x && eventX < (x+width) && eventY > y && eventY < (y+height)){ 
      initialX=x; 
      initialY=y; 
      break; 
     }else{ 
      return false; 
     } 
    case MotionEvent.ACTION_MOVE: 
     //determine if piece should move horizontally or vertically 
     if(width>height){ 
      for (Pieces piece : aPieces) { 
       //if object equals itself in array, skip to next object 
       if(piece==this){ 
        continue; 
       } 
       //if next to another piece, 
       //do not allow to move any further towards said piece 
       if(eventX<x&&(x==piece.right+1)){ 
        return false; 
       }else if(eventX>x&&(x==piece.x-width-1)){ 
        return false; 
       } 
       //move normally if no collision 
       //if collision, do not allow to move through other piece 
       if(collides(this,piece)==false){ 
        x = (eventX-(width/2)); 
       }else if(collidesLeft(this,piece)){ 
        x = piece.right+1; 
        break; 
       }else if(collidesRight(this,piece)){ 
        x = piece.x-width-1; 
        break; 
       } 
      } 
      break; 
     }else if(height>width){ 
      for (Pieces piece : aPieces) { 
       if(piece==this){ 
        continue; 
       }else if(collides(this,piece)==false){ 
        y = (eventY-(height/2)); 
       }else if(collidesUp(this,piece)){ 
        y = piece.bottom+1; 
        break; 
       }else if(collidesDown(this,piece)){ 
        y = piece.y-height-1; 
        break; 
       } 
      } 
     } 
     invalidate(); 
     break; 

    case MotionEvent.ACTION_UP: 
     // end move 
     if(this.moves()){ 
     GameView.counter++; 
     } 
     initialX=x; 
     initialY=y; 
     break; 
     } 
    // parse puzzle 
    invalidate(); 
    return true; 
    } 

这需要的onDraw过程中发生:

width = sizedBitmap.getWidth(); 
height = sizedBitmap.getHeight(); 
right = x+width; 
bottom = y+height; 

我的碰撞试验方法看起来像这样用不同的数学每个:

private boolean collidesDown(Pieces piece1, Pieces piece2){ 
    float x1 = piece1.x; 
    float y1 = piece1.y; 
    float r1 = piece1.right; 
    float b1 = piece1.bottom; 
    float x2 = piece2.x; 
    float y2 = piece2.y; 
    float r2 = piece2.right; 
    float b2 = piece2.bottom; 

    if((y1<y2)&&(y1<b2)&&(b1>=y2)&&(b1<b2)&&((x1>=x2&&x1<=r2)||(r1>=x2&&x1<=r2))){ 
     return true; 
    }else{ 
     return false; 
    } 
} 

private boolean collides(Pieces piece1, Pieces piece2){ 
    if(collidesLeft(piece1,piece2)){ 
     return true; 
    }else if(collidesRight(piece1,piece2)){ 
     return true; 
    }else if(collidesUp(piece1,piece2)){ 
     return true; 
    }else if(collidesDown(piece1,piece2)){ 
     return true; 
    }else{ 
     return false; 
    } 
} 

作为第二个问题,应该我的x,y,右,底部,宽度,高度变量是整数,而不是浮动像他们 现在? 此外,如何更好地实施的任何建议将不胜感激,即使与问题无关!预先感谢您的帮助,并通过这么长的问题坐下来!

更新:
我得到它与下面的代码的工作几乎是完美的(这不包括垂直部分的代码):

@Override 
public boolean onTouchEvent(MotionEvent event){ 

    float eventX = event.getX(); 
    float eventY = event.getY(); 

    switch (event.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
     //check if touch is on piece 
     if (eventX > x && eventX < (x+width) && eventY > y && eventY < (y+height)){ 
      initialX=x; 
      initialY=y; 
      break; 
     }else{ 
      return false; 
     } 
    case MotionEvent.ACTION_MOVE: 
     //determine if piece should move horizontally or vertically 
     if(width>height){ 
      for (Pieces piece : aPieces) { 
       //if object equals itself in array, skip to next object 
       if(piece==this){ 
        continue; 
       } 
       //check if there the possibility for a horizontal collision 
       if(this.isAllignedHorizontallyWith(piece)){ 
        //check for and handle collisions while moving left 
        if(this.isRightOf(piece)){ 
         if(eventX>piece.right+(width/2)){ 
          x = (int)(eventX-(width/2)); //move normally 
         }else{ 
          x = piece.right+1; 
         } 
        } 
        //check for and handle collisions while moving right 
        if(this.isLeftOf(piece)){ 
         if(eventX<piece.x-(width/2)){ 
          x = (int)(eventX-(width/2)); 
         }else{ 
          x = piece.x-width-1; 
         } 
        } 
        break; 
       }else{ 
        x = (int)(eventX-(width/2)); 
       } 

这段代码的唯一问题是,它仅检测移动件和另一个之间的碰撞(优先选择左侧的碰撞)。如果有一块在左侧碰撞而另一块在右侧碰撞,则它只会检测到与左侧碰撞的碰撞。我认为这是因为一旦发现可能发生的碰撞,它会处理它,而不会完成循环遍历所有片断的数组。我如何得到它在同一时间检查多个可能的碰撞?

+0

我已经回答了您的更新以下的问题 – Timo 2011-03-16 11:52:01

回答

1

我最好的猜测是,拖动的部分被拖入另一部分,然后移回,因此不再发生碰撞,然后再次拖入另一部分。至少这是我所期望的,如果在绘图之后发生碰撞响应。

在一个侧面说明,下面的代码让我纳闷了一下:在彩车

  //if next to another piece, 
      //do not allow to move any further towards said piece 
      if(eventX<x&&(x==piece.right+1)){ 
       return false; 
      }else if(eventX>x&&(x==piece.x-width-1)){ 
       return false; 
      } 

检查平等(==)通常是一个非常糟糕的主意,看到的“浮点精度”主题不计其数。备选方案a)使用整数而不是浮点数。 b)使用范围比较(>=等)。不要使用(a),但使用微小(时间)步骤等有许多缺点。

尝试使用相同的方法与触摸检测来代替:

//check if touch is on piece 
    if (eventX > x && eventX < (x+width) && eventY > y && eventY < (y+height)) 
+0

感谢您的帮助,我用新的代码更新了我的问题在底部,我最终使用了整数而不是浮点数,让我知道你的想法, – Amplify91 2011-03-16 00:10:42

1

你应该能够做到这一点更简单:)

请注意,我做了你如何存储方向的一些假设运动,你可能不得不适应你的喜好。

另请注意,我将piece1视为我们所关注的移动块,而piece2仅将它视为与它碰撞的对象。所以第一块被重新定位,第二块不是。

// Horizontal: 
if(piece1.isMovingLeft) 
    piece1.x += Math.max(0, piece2.right - piece1.x); 
else if(piece1.isMovingRight) 
    piece1.x -= Math.max(0, piece1.right - piece2.x); 

// Vertical: 
if(piece1.isMovingUp) 
    piece1.y -= Math.max(0, piece2.bottom - piece1.y); 
else if(piece1.isMovingDown) 
    piece1.y += Math.max(0, piece1.bottom - piece2.y) 

我在这里做的是:

  • 如果片在某一方向移动,我们在相反的方向移动回(一点点),以补偿(可能的)碰撞。我们需要将其重新移回恰好重叠的像素数量。
  • 例如,向左移动时,重叠量是第二个对象的右侧减去第一个对象的左侧。 (负表示不重叠。)
  • Math.max(0,重叠)确保所述负值变为0,即没有碰撞导致无补偿。
  • 然后以与运动相反的方向应用补偿,从而有效地将piece1从piece2中取出。 (然后您可以选择反转其移动方向或以任何其他方式进行响应。)

您可以将这个简单的例程应用于每个可能的两部分组合,并且它们将不再穿透。

我希望这可以帮助你!

+0

谢谢你的回答!我希望我可以接受多于一个,因为这非常有帮助。我已在底部附近的更新中发布。 – Amplify91 2011-03-16 00:08:44

0

在回答第二个问题:

与此代码唯一的问题是,它仅检测所述移动件和另外一个之间的碰撞(具有优先于一个在左边)。如果有一块在左侧碰撞而另一块在右侧碰撞,则它只会检测到与左侧碰撞的碰撞。

您的循环中有一个break语句 - 具体来说,在左侧碰撞检查。 ;)

+0

break语句实际上发生在if(this.isAllignedHorizo​​ntallyWith(piece))的末尾,它需要在那里。我正在考虑为循环检查另一个不同的部分,如果它已经找到了一个向左,反之亦然。思考? – Amplify91 2011-03-16 19:37:21

+0

你说得对,它不在左侧碰撞检查中。然而,它是_is_导致你的问题!你会发现,当有另外两个水平对齐的棋子(一个在你的左边,另一个在你的右边)时,你的棋子检测到与第一个棋子水平对齐(左边那个,就像你在你的问题中说的那样)。这会导致您到达break语句,即停止循环并防止其他碰撞检查发生。删除break语句是否可以解决您的问题? – Timo 2011-03-21 14:12:04

+0

我尝试删除它,但它给了我其他问题。我现在所得到的解决方案似乎完美地工作(敲木头)。我改变了isLeftOf()返回true,如果它是一个片断的左边,那个片段是最接近的片段被水平地排列并且在右边(或者如果它是唯一的片段)。如果不是,则继续检查可能的碰撞。 – Amplify91 2011-03-21 18:14:09

0

我做了一些改变,到目前为止,它一直在完美工作。这是我的新代码:

case MotionEvent.ACTION_MOVE: 
     //determine if piece should move horizontally or vertically 
     if(width>height){ 
      for (Pieces piece : aPieces) { 
       //if object equals itself in array, skip to next object 
       if(piece==this){ 
        continue; 
       } 
       //check if there the possibility for a horizontal collision 
       if(this.isAllignedHorizontallyWith(piece)){ 
        //check for and handle collisions while moving left 
        if(this.isRightOf(piece)){ 
         if(eventX>piece.right+(width/2)){ 
          x = (int)(eventX-(width/2)); //move normally 
          continue; 
         }else{ 
          x = piece.right+1; 
         } 
        }else if(this.isLeftOf(piece)){ //check for and handle collisions while moving right 
         if(eventX<piece.x-(width/2)){ 
          x = (int)(eventX-(width/2)); 
          continue; 
         }else{ 
          x = piece.x-width-1; 
         } 
        }else{ 
         continue; 
        } 
        break; 
       }else{ 
        x = (int)(eventX-(width/2)); 
       } 
      } 
     } 
     if(height>width){ 
      for (Pieces piece : aPieces) { 
       //if object equals itself in array, skip to next object 
       if(piece==this){ 
        continue; 
       } 
       //check if there the possibility for a vertical collision 
       if(this.isAllignedVerticallyWith(piece)){ 
        //check for and handle collisions while moving up 
        if(this.isBelow(piece)){ 
         if(eventY>piece.bottom+(height/2)){ 
          y = (int)(eventY-(height/2)); //move normally 
          continue; 
         }else{ 
          y = piece.bottom+1; 
         } 
        }else if(this.isAbove(piece)){ //check for and handle collisions while moving down 
         if(eventY<piece.y-(height/2)){ 
          y = (int)(eventY-(height/2)); 
          continue; 
         }else{ 
          y = piece.y-height-1; 
         } 
        }else{ 
         continue; 
        } 
        break; 
       }else{ 
        y = (int)(eventY-(height/2)); 
       } 
      } 
     } 
     invalidate(); 
     break; 

而这些都是我使用的方法:

private boolean isLeftOf(Pieces piece) { 
    int r1 = this.right; 
    int x2 = piece.initialX; 
    boolean bool = false; 
    if (r1<=x2){ 
     for(Pieces piece2: aPieces){ 
      if (piece2==piece || piece2==this){ 
       continue; 
      } 
      if (this.isAllignedHorizontallyWith(piece2) && r1<=piece2.initialX){ 
       if (piece.x>piece2.x){ 
        bool = false; 
        break; 
       }else{ 
        bool = true; 
        continue; 
       } 
      }else{ 
       bool = true; 
      } 
     } 
    }else{ 
     bool = false; 
    } 
    return bool; 
} 

private boolean isRightOf(Pieces piece) { 
    //True means that "this" is right of "piece" and "piece" is farther right than other possible pieces 
    int x1 = this.initialX; 
    int r2 = piece.right; 
    boolean bool = false; 

    if (x1>=r2){ 
     for(Pieces piece2: aPieces){ 
      if (piece2==piece || piece2==this){ 
       continue; 
      } 
      if (this.isAllignedHorizontallyWith(piece2) && x1>=piece2.right){ 
       if (piece.x<piece2.x){ 
        bool = false; 
        break; 
       }else{ 
        bool = true; 
        continue; 
       } 
      }else{ 
       bool = true; 
      } 
     } 
    }else{ 
     bool = false; 
    } 
    return bool; 
} 

private boolean isBelow(Pieces piece){ 
    int y1 = this.initialY; 
    int b2 = piece.bottom; 
    boolean bool = false; 
    if (y1>=b2){ 
     for(Pieces piece2: aPieces){ 
      if (piece2==piece || piece2==this){ 
       continue; 
      } 
      if (this.isAllignedVerticallyWith(piece2) && y1>=piece2.bottom){ 
       if (piece.y<piece2.y){ 
        bool = false; 
        break; 
       }else{ 
        bool = true; 
        continue; 
       } 
      }else{ 
       bool = true; 
      } 
     } 
    }else{ 
     bool = false; 
    } 
    return bool; 
} 

private boolean isAbove(Pieces piece){ 
    int y2 = piece.initialY; 
    int b1 = this.bottom; 
    boolean bool = false; 
    if (b1<=y2){ 
     for(Pieces piece2: aPieces){ 
      if (piece2==piece || piece2==this){ 
       continue; 
      } 
      if (this.isAllignedVerticallyWith(piece2) && b1<=piece2.y){ 
       if (piece.y>piece2.y){ 
        bool = false; 
        break; 
       }else{ 
        bool = true; 
        continue; 
       } 
      }else{ 
       bool = true; 
      } 
     } 
    }else{ 
     bool = false; 
    } 
    return bool; 
} 

private boolean isAllignedHorizontallyWith(Pieces piece) { 

    int y1 = this.y; 
    int b1 = this.bottom; 
    int y2 = piece.y; 
    int b2 = piece.bottom; 

    if((y1>=y2&&y1<=b2)||(b1>=y2&&b1<=b2)){ 
     return true; 
    }else{ 
     return false; 
    } 
} 

private boolean isAllignedVerticallyWith(Pieces piece) { 

    int x1 = this.x; 
    int r1 = this.right; 
    int x2 = piece.x; 
    int r2 = piece.right; 

    if((x1>=x2&&x1<=r2)||(r1>=x2&&r1<=r2)){ 
     return true; 
    }else{ 
     return false; 
    } 
} 

这可以更好地评论说,也许做了简单得多,但我离开这里以供参考。

0

没什么刚刚拿到您的ImageView s Left,right,top,bottom, and intersect with current x and y and find this solution more about this go to this link it S还帮我希望你们得到了这个链接

/技术/游戏编程/冲突检测算法-r754" > HTTP上的正确答案:// WWW。 gamedev.net/page/resources//technical/game-programming/collision-detection-algorithm-r754