Обнаружение столкновения с линиями и ограничение движения - PullRequest
0 голосов
/ 13 ноября 2018

Я делаю игру с libGDX на Java. Я пытаюсь сделать обнаружение столкновения. Как вы можете видеть на картинке, у меня есть линия, которая представляет собой стену и игрока с указанным радиусом. Желаемая позиция - это следующая локация, в которой игрок пытается оказаться. Но поскольку там есть стена, он помещается в Фактическую позицию, которая находится на векторе Velocity, но ближе к предыдущей локации. Я пытаюсь выяснить, как я могу обнаружить эту более близкую позицию? enter image description here

Моя попытка:

private void move(float deltaTime) {
    float step;
    beginMovementAltitude();
    if (playerComponent.isWalking())
        step = handleAcceleration(playerComponent.getSpeed() + playerComponent.getAcceleration());
    else step = handleDeacceleration(playerComponent.getSpeed(), playerComponent.getAcceleration());
    playerComponent.setSpeed(step);
    if (step == 0) return;
    takeStep(deltaTime, step, 0);
}
private void takeStep(float deltaTime, float step, int rotate) {
    Vector3 position = playerComponent.getCamera().position;
    float x = position.x;
    float y = position.y;
    int radius = playerComponent.getRadius();
    auxEnvelope.init(x, x + radius, y, y + radius);
    List<Line> nearbyLines = lines.query(auxEnvelope);
    float theta;
    int numberOfIntersections = 0;
    float angleToMove = 0;
    Gdx.app.log(step + "", "");
    for (Line line : nearbyLines) {
        VertexElement src = line.getSrc();
        VertexElement dst = line.getDst();
        auxVector3.set(playerComponent.getCamera().direction);
        auxVector3.rotate(Vector3.Z, rotate);
        float nextX = x + (step * deltaTime) * (auxVector3.x);
        float nextY = y + (step * deltaTime) * playerComponent.getCamera().direction.y;
        float dis = Intersector.distanceLinePoint(src.getX(), src.getY(), dst.getX(), dst.getY(), nextX, nextY);
        boolean bodyIntersection = dis <= 0.5f;
        auxVector21.set(src.getX(), src.getY());
        auxVector22.set(dst.getX(), dst.getY());
        auxVector23.set(nextX, nextY);
        if (bodyIntersection) {
            numberOfIntersections++;
            if (numberOfIntersections > 1) {
                return;
            }
            theta = auxVector22.sub(auxVector21).nor().angle();
            float angle = (float) (180.0 / MathUtils.PI * MathUtils.atan2(auxVector23.y - position.y, auxVector23.x - position.x));
            if (angle < 0) angle += 360;
            float diff = (theta > angle) ? theta - angle : angle - theta;
            if (step < 0) step *=-1;
            angleToMove = (diff > 90) ? theta + 180 : theta;
        }
    }
    if (numberOfIntersections == 0) {
        moveCameraByWalking(deltaTime, step, rotate);
    } else {
        moveCameraInDirection(deltaTime, step, angleToMove);
    }
}

1 Ответ

0 голосов
/ 20 ноября 2018

Идея состоит в том, чтобы найти пересечение траектории центра объекта и линии, перемещенной по радиусу круга, см. Эту картинку.

this is the idea

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

nx = ay - by
ny = bx - ax

Если линия определена каноническим уравнением, то коэффициенты на x и y определяют нормаль, если я правильно запомнил.

Когда нормаль найдена, нам нужно ее нормализовать - установите длину в 1, разделив координаты на длину вектора.Пусть это будет n .

Затем мы спроецируем начальную точку, желаемую точку и случайно выбранную точку на линии на n , рассматривая их как радиус-векторов.

Проекция вектора a на вектор b равна

project (a, b) = scalar_product (a, b) / length (b)**2 * b

, но поскольку b равна n , длина которого равна 1, мы не будем применять деление, а также мы хотим только найти длину результата, мы не умножаем на b .Таким образом, мы вычисляем только скалярное произведение с n для каждой из трех вышеупомянутых точек, получая три числа, пусть s будет результатом для начальной точки, d для нужной точки, l для выбранной точки на линии.

Тогда мы должны изменить l на радиус окружности:

if      (s < d) l -= r;
else if (s > d) l += r;

Если s = d , ваш объект движется параллельно вдоль линии, поэтому линия не может препятствовать его движению.Это очень маловероятный случай, но с ним нужно разобраться.

Кроме того, важно, если l изначально был между s и d , но после изменениямежду ними больше нет, это особый случай, который вы можете обработать (например, ограничить движение объекта)

corner case

А затем, вы должны вычислить (d - s) / (l - s) .

Если результат больше или равен 1, объект не достигнет линии.
Если результат находится между 0 и1, линия препятствует движению, и результат указывает на часть пути, по которому будет проходить объект.0.5 означает, что объект остановится на полпути.
Если результат отрицательный, это означает, что линия находится за объектом и не будет препятствовать движению.

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

...