Java Tile Game - Обнаружение столкновений

Я делаю java-игру, используя плитки. У меня проблема с элементом столкновения. Я определяю прямоугольники для каждой плитки на карте и еще один прямоугольник для игрока. У меня проблемы с тем, чтобы знать, с какой стороны идет игрок, когда он попадает в прямоугольник, а затем отталкивать игрока в том направлении, откуда он пришел. Я уже создал метод, который проверяет, насколько персонаж находится внутри прямоугольника, чтобы он мог знать, сколько его нужно вытолкнуть, но я не могу понять, как определить, с какой стороны идет персонаж.

вот мой текущий метод столкновения - обратите внимание, что rect1 - это персонаж, а rect2 - это плитка

public void collision(Rectangle rect1, Rectangle rect2) {
    float xAdd;
    float xAdd2;
    float yAdd;
    float yAdd2;

    boolean hitRight = false;
    boolean hitLeft = false;
    boolean hitTop = false;
    boolean hitBot = false;

    Vector2f rect1Origin = new Vector2f(rect1.x, rect1.y);
    Vector2f rect2Origin = new Vector2f(rect2.x, rect2.y);
    Vector2f rect1Mid = new Vector2f((rect1.x + rect1.width) / 2,(rect1.y + rect1.height) / 2);
    Vector2f rect2Mid = new Vector2f((rect2.x + rect2.width) / 2,(rect2.y + rect2.height) / 2);

    Vector2f rect1A = new Vector2f(rect1Origin.x + rect1.width, rect1.y);
    Vector2f rect1B = new Vector2f(rect1Origin.x, rect1Origin.y+ rect1.height);
    Vector2f rect1C = new Vector2f(rect1Origin.x + rect1.width,rect1Origin.y + rect1.height);

    Vector2f rect2A = new Vector2f(rect2Origin.x + rect2.width, rect2.y);
    Vector2f rect2B = new Vector2f(rect2Origin.x, rect2Origin.y
            + rect2.height);
    Vector2f rect2C = new Vector2f(rect2Origin.x + rect2.width,
            rect2Origin.y + rect2.height);

    xAdd = rect2C.x - rect1B.x;
    xAdd2 = rect1C.x - rect2B.x;

    yAdd = rect2A.y - rect1B.y;
    yAdd2 = rect2C.y - rect1A.y;


    if (rect1Mid.y < rect2Mid.y) {
        if (rect1.intersects(rect2)) {
            y_pos += yAdd;
        }
    }
    if (rect1Mid.y > rect2Mid.y) {
        if (rect1.intersects(rect2)) {
            System.out.println(yAdd2);
            y_pos += yAdd2;
        }

    }
    if(rect1Mid.x > rect2Mid.x){
        if(rect1.intersects(rect2)){
            hitRight = true; x_pos += xAdd;
        }
    } 
    if(rect1Mid.x< rect2Mid.x){ 
          if(rect1.intersects(rect2)) {
              x_pos += -xAdd2;
          } 
    }
}

Любая помощь будет принята с благодарностью

Спасибо


person matthewtory    schedule 29.04.2012    source источник


Ответы (4)


Сохраните две позиции для вашего персонажа - где он находится (последний кадр, ход и т. Д.), И куда вы хотите его переместить. Затем перемещайте его только в том случае, если вы не обнаруживаете столкновения - если вы не допускаете поврежденное состояние, вам не нужно его исправлять.

Изменить: метод collision должен быть boolean - это нужно делать до фактического перемещения персонажа, например

if (!collision(character, tile))
{
    doMove(character);
}
else
{
    //custom handling if required
}

Edit2: предыдущее будет работать только для небольшого шага, если вам нужен частичный ход, вам действительно нужно знать исходное положение персонажа, например move(originalPosition, desiredPosition, tile), где вы можете определить направление из originalPosition и tile.

Суть в том, что вы фактически не перемещаете персонажа, пока не получите для него подходящую позицию.

person jmruc    schedule 29.04.2012
comment
Это приведет к странной ситуации, зависящей от интервала временного шага. - person Matzi; 29.04.2012
comment
@Matzi Не совсем, дело в проверке перед выполнением какой-либо пользовательской команды - если она не проверяется, вы можете выполнить индивидуальную обработку. - person jmruc; 29.04.2012
comment
@KirilRaychev Я не совсем понимаю, что вы пытаетесь сказать - person matthewtory; 29.04.2012
comment
@ grimrader22 Я попытался прояснить ситуацию, пожалуйста, спросите, недостаточно ли она хороша. - person jmruc; 29.04.2012
comment
@KirilRaychev По личному опыту не так. :) Может быть небольшой эввект, если шаг небольшой, но проблема есть. - person Matzi; 29.04.2012
comment
@KirilRaychev Это все равно не решит проблему, потому что, если персонаж попадает в прямоугольник, он не сможет отойти от прямоугольника, потому что метод domove не сможет вызвать. Вот почему мне нужно найти сторону персонажа, чтобы я мог отключить эту часть метода domove. - person matthewtory; 29.04.2012

Прежде всего, любой Sprite (здесь символ и плитка) должен иметь четыре члена: xPos, yPos, xVec, yVec. Имея это в виду, вы знаете, где был и будет персонаж в следующем кадре.

sprite.xPos += sprite.xVec;
sprite.yPos += sprite.yVec;

Также обратите внимание на свой код, вам следует улучшить предложения (например, вы отметите if (rect1.intersects (rect2)) в четырех операторах if). Вместо этого проверьте только один раз для пересекается (), и внутреннюю проверку для каждого возможного случая (левое, правое, верхнее, нижнее попадание).

Наконец, метод столкновения должен получить 2 объекта-спрайта (например, sprite1 и sprite2), а затем проверить наличие метода corrects (), с учетом исходного положения обоих спрайтов и его векторов. Если спрайты пересекаются в новой позиции, вы соответственно инвертируете вектор объекта.

person Federico Cristina    schedule 29.04.2012
comment
Что представляют собой переменные xVec и yVec? - person matthewtory; 30.04.2012
comment
Извините, я не совсем понял, это векторы направления спрайта (вы также можете использовать Vector2f или Point2D). Значение можно интерпретировать как скорость спрайта в каждом направлении. Таким образом, если персонаж попадает в плитку точно слева, xVec - положительное значение, а yVec - 0; а после попадания xVec должно иметь то же значение, но теперь отрицательное. Сообщите мне, если вам понадобится дополнительная помощь. - person Federico Cristina; 30.04.2012
comment
Я не совсем понимаю, о чем вы говорите, извините, если я веду себя глупо. Будет ли x и y Vecs скоростью? - person matthewtory; 01.05.2012
comment
Правильно. Предположим, спрайт находится на xPos = 10, yPos = 20 и xVec = 3, yVec = 2. Итак, в следующем кадре xPos будет 13, а yPos будет 22. После этого xVec и yVec могут быть одинаковыми (a корабль в космосе), уменьшенное (трение, сила тяжести и т. д.) или увеличенное (дроссельная заслонка). В случае столкновения xVec и yVec изменятся в зависимости от положения другого спрайта. Итак, вернемся к примеру, если у персонажа xVec = 3, а yVec = 1 и он попадает слева (character.xPos ‹tile.xPos), отскок достигается при установке -3 на xVec и оставлении yVec на 1 (вы можете добавить немного случайного, если хотите) - person Federico Cristina; 01.05.2012

Предполагая, что вы можете обнаружить столкновение с вашим кодом. Вот способ определить положение вашего персонажа относительно прямоугольного объекта.

  1. Найдите середину прямоугольника. (Rx, Ry).

  2. Найдите середину спрайта вашего персонажа. (Sx, Sy).

  3. Теперь вы можете сравнить Rx, Ry, Sx, Sy, чтобы определить, с какой стороны Sx и Sy у Rx и Ry.

  4. например:

    if Sx < Rx then
      Sx = left side of Rx
    
person teon    schedule 03.05.2012

Сам боролся с этим, но после некоторого размышления, эта проверка может быть выполнена, если все ваши объекты находятся в списке, в моем коде мне пришлось заблокировать игроку возможность перемещаться по блокам. Все блоки находятся в списке. Теперь, проверяя положение игроков, я создаю новый прямоугольник и помещаю игроков на один кадр вперед в направлении, в котором произойдет обновление в следующем кадре, если прямоугольник пересекается, этого обновления для этого направления не произойдет. Вот мой код для этого:

    if (isKeyPressed(KeyEvent.VK_LEFT)) {
        if(!collisionWithBlocks(1)){
            pl.x = pl.x - updatePlayerPosition;
        }
    }
    if (isKeyPressed(KeyEvent.VK_RIGHT)) {
        if(!collisionWithBlocks(0)){
            pl.x = pl.x + updatePlayerPosition;
        }
    }
    if (isKeyPressed(KeyEvent.VK_UP)) {
        if(!collisionWithBlocks(3)){
            pl.y = pl.y - updatePlayerPosition;
        }
    }
    if (isKeyPressed(KeyEvent.VK_DOWN)) {
        if(!collisionWithBlocks(2)){
            pl.y = pl.y + updatePlayerPosition;
        }
    }

collisionWithBlocks ():

public boolean collisionWithBlocks(int side){
    for(Block b : main.blocks){
        Rectangle block = b.getBounds();
        Rectangle player = null;
        if(side == 0){
            player = new Rectangle(pl.x + updatePlayerPosition, pl.y, pl.getWidth(), pl.getHeight());
        } else if(side == 1){
            player = new Rectangle(pl.x - updatePlayerPosition, pl.y, pl.getWidth(), pl.getHeight());
        } else if(side == 2){
            player = new Rectangle(pl.x, pl.y + updatePlayerPosition, pl.getWidth(), pl.getHeight());
        } else if(side == 3){
            player = new Rectangle(pl.x, pl.y - updatePlayerPosition, pl.getWidth(), pl.getHeight());
        }

        if(player.intersects(block)){
            return true;
        }
    }

    return false;
}

updatePlayerPosition равно 2, и в моем коде меняются, но об этом хватит. В вашем случае я рекомендую поместить объекты в список, а затем проверить, как это сделал я.

person hultberg    schedule 12.08.2013