Проблемы со столкновением с игрой-головоломкой - PullRequest
4 голосов
/ 13 марта 2011

Я работаю над игрой для Android, похожей на Rush Hour / Traffic Jam / Blocked головоломки. Доска представляет собой квадрат, содержащий прямоугольные фигуры. Длинные части могут двигаться только горизонтально, а высокие могут двигаться только вертикально. Цель состоит в том, чтобы освободить красную фигуру и убрать ее с доски. Эта игра - мой второй проект по программированию на любом языке, поэтому любые советы и рекомендации будут оценены вместе с вашим ответом.

У меня есть класс для игровых фигур, который называется Pieces и который описывает их размеры и изображение на экране, дает им возможность перетаскивания, обнаруживает и обрабатывает столкновения.

Затем у меня есть класс активности GameView, который создает мой макет и создает объекты Pieces для добавления в RelativeLayout, называемый Board. Я подумал о том, чтобы сделать Совет своим собственным классом, но пока не нуждался.

Вот как выглядит моя работа:
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 быть целыми числами вместо float, как сейчас? Кроме того, любые предложения о том, как реализовать вещи лучше, будет принята с благодарностью, даже если не имеет отношения к вопросу! Заранее благодарим за помощь и за то, что сидели в таком длинном вопросе!

Обновление:
Я получил почти идеальную работу со следующим кодом (сюда не входит код для вертикальных фрагментов):

@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 ]

2 голосов
/ 14 марта 2011

Мое лучшее предположение заключается в том, что перетаскиваемый кусок перетаскивается в другой кусок, затем перемещается назад, чтобы он больше не сталкивался, а затем снова перетаскивается в другой кусок. По крайней мере, это то, что я ожидаю, если реакция на столкновение произойдет после рисования.

Кстати, следующий код заставляет меня задуматься:

            //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;
            }

Проверка на равенство (==) на числах с плавающей точкой, как правило, очень плохая идея, см. Миллионы тем с "точностью с плавающей точкой". Альтернатива а) использовать целые вместо плавающих. б) использовать ранжированные сравнения (>= и т. д.). Не используйте (а), хотя есть много недостатков с крошечными (временными) шагами и тому подобным.

Попробуйте использовать тот же подход, что и при обнаружении касания:

    //check if touch is on piece
    if (eventX > x && eventX < (x+width) && eventY > y && eventY < (y+height))
1 голос
/ 14 марта 2011

Вы должны быть в состоянии сделать это проще:)

Обратите внимание, что я сделал некоторые предположения о том, как вы сохраняете направление движения, и вам, возможно, придется настроить его по своему вкусу.

Также обратите внимание, что я рассматриваю кусок 1 как движущуюся часть, с которой мы имеем дело, а кусок 2 просто как объект, с которым он сталкивается. Итак, 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. (Затем вы можете изменить направление движения или реагировать любым другим способом.)

Вы можете применить эту простую процедуру к каждой возможной комбинации двух фигур, и они больше не будут проникать.

Надеюсь, это вам поможет!

0 голосов
/ 11 июля 2012

Ничего, просто получите ваше изображение. 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 также поможет. Надеюсь, вы получили правильный ответ по этой ссылке

/ technical / game-Programming / Collil-Detection -gorithm-r754 "> http://www.gamedev.net/page/resources//technical/game-programming/collision-detection-algorithm-r754

0 голосов
/ 21 марта 2011

Я сделал несколько изменений, и до сих пор он работал отлично.Вот мой новый код:

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 голосов
/ 16 марта 2011

В ответ на ваш второй вопрос:

Единственная проблема с этим кодом состоит в том, что он обнаруживает только столкновения между движущейся деталью и одной другой (предпочтительнее одной слева).Если есть фрагмент, с которым нужно столкнуться слева, а другой справа, он будет обнаруживать столкновения только с фрагментом слева.

В вашем цикле есть оператор break - в частности, в левая проверка столкновения.;)

...