拖放益智游戏的碰撞问题

时间:2011-03-13 20:55:37

标签: java android drag-and-drop collision-detection

我正在开发一款类似于尖峰时刻/交通拥堵/封锁益智游戏的Android游戏。该板是一个包含矩形块的正方形。长件只能水平移动,而高件只能垂直移动。目的是释放红色片并将其移出板。这个游戏只是我用任何语言编写的第二个编程项目,因此任何提示或最佳实践都会与您的答案一起受到赞赏。

我有一个名为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,right,bottom,width,height变量应该是int而不是像现在这样的浮点数? 此外,任何有关如何更好地实施事情的建议都将受到高度赞赏,即使与问题无关!提前感谢您的帮助和坐下这么长的问题!

更新
我已经使用以下代码几乎完美地工作了(这不包括垂直部分的代码):

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

此代码的唯一问题是它仅检测移动件与另一个之间的碰撞(优先于左侧的碰撞)。如果左侧和右侧碰撞有一块,它只会检测左侧的碰撞。我认为这是因为一旦发现可能的碰撞,它就会处理它而不会完成循环通过包含所有碎片的数组。如何让它同时检查多个可能的碰撞?

5 个答案:

答案 0 :(得分:2)

我最好的猜测是将被拖动的棋子拖入另一块,然后向后移动,使其不再碰撞,然后再次拖入另一块。至少这是我在绘图后发生碰撞响应时的预期。

在旁注中,以下代码让我有点疑惑:

            //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)使用int而不是浮点数。 b)使用远程比较(>=等)。不要使用(a)但是,微小(时间)步骤等有许多缺点。

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

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

答案 1 :(得分:1)

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

请注意,我对如何存储移动方向做了一些假设,您可能需要根据自己的喜好进行调整。

另请注意,我将piece1视为我们关注的移动部件,而piece2仅仅作为它碰撞的对象。所以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,即没有碰撞导致无补偿。
  • 然后在与运动相反的方向上施加补偿,有效地将棋子1从棋子2中取出。 (然后您可以选择反转其移动方向或以任何其他方式做出响应。)

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

我希望这会帮助你!

答案 2 :(得分:0)

回复你的第二个问题:

  

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

循环中有一个break语句 - 特别是在 left 碰撞检查中。 ;)

答案 3 :(得分: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;   
    }
}

这可以更好地评论,可能做得更简单,但我将它留在这里作为参考。

答案 4 :(得分: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" &GT; HTTP://www.gamedev.net/page/resources//technical/game-programming/collision-detection-algorithm-r754