AS3 - Как мне найти, где линия сталкивается с прямоугольным объектом?

Я разрабатываю игру с Flixel в качестве основы, и часть того, что мне нужно, - это способ проверки столкновений вдоль линии (в частности, линии от точки A до точки B). Лучший способ объяснить это - у меня есть лазерный луч, стреляющий с одного корабля на другой объект (или в точку в пространстве, если ничто не перекрывает линию). Я хочу, чтобы линия доходила только до тех пор, пока не достигнет объекта. Как я могу определить математически / программно, где вдоль линии линия сталкивается с объектом?

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

Изменить: Прежде чем проверять объект на предмет столкновения с самой линией, я бы сначала удалил любые объекты, не входящие в ограничивающий прямоугольник линии - определяемый как x самой левой точки, y самой верхней точки, x х самая правая точка, а у самой нижней точки. Это ограничит проверки на столкновение строк несколькими объектами.

Отредактируйте снова: мой вопрос, кажется, все еще не совсем ясен, извините. Некоторые из решений, вероятно, будут работать, но я ищу простое, предпочтительно математическое решение. И когда я говорю "прямоугольник", я имею в виду тот, чьи стороны привязаны к осям x и y, а не вращающийся прямоугольник. Таким образом, линия не является прямоугольником шириной 0, если она не находится под углом 90 или -90 градусов (при условии, что 0 градусов указывают справа от экрана).

Вот визуальное представление того, что я пытаюсь найти:Обнаружение столкновения линии

4 ответа

Решение

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

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

typedef struct {
    GLfloat A;
    GLfloat B;
    GLfloat C;
} Line;

static inline Line LineMakeFromCoords(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) {
    return (Line) {y2-y1, x1-x2, (y2-y1)*x1+(x1-x2)*y1};
}

static inline Line LineMakeFromSegment(Segment segment) {
    return LineMakeFromCoords(segment.P1.x,segment.P1.y,segment.P2.x,segment.P2.y);
}

Затем проверьте, пересекаются ли они

static inline Point2D IntersectLines(Line line1, Line line2) {
    GLfloat det = line1.A*line2.B - line2.A*line1.B;
     if(det == 0){
    //Lines are parallel
            return (Point2D) {0.0, 0.0};  // FIXME should return nil
     }else{
            return (Point2D) {(line2.B*line1.C - line1.B*line2.C)/det, (line1.A*line2.C - line2.A*line1.C)/det};
     }  
}

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

Математика все в Википедии, проверьте там, если вам нужно больше информации.

Редактировать:

Дополнение для последующего комментария:

Как и перед тестированием вашего сегмента на предмет столкновения со всеми четырьмя сегментами прямоугольника, вы получите один из 3 случаев:

  1. Никакой точки столкновения / столкновения нет на экране (помните, что тесты столкновения направлены против линий, а не сегментов линий, и линии всегда будут пересекаться, если они не параллельны), насмехайтесь над игроком за отсутствие:-)
  2. Одно столкновение, нарисуйте / сделайте все, что вы хотите, сегмент, который вы запрашиваете, будет AC (точка столкновения C)
  3. Два столкновения: проверьте размер каждого результирующего сегмента (A-C1) и (A-C2), используя что-то вроде приведенного ниже кода, и оставьте тот, у которого самый короткий размер.

    static inline float SegmentSizeFromPoints(Vertice3D P1, Vertice3D P2) {
         return sqrtf(powf((P1.x - P2.x),2.0) + pow((P1.y - P2.y),2.0));
    }
    

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

  1. Найти формулу для линии y = ((y2 - y1)/(x2 - x1)) * (x - x1) + y1
  2. Найдите ограничивающие рамки для ваших спрайтов
  3. Для ограничивающей рамки каждого спрайта:
  4. Для каждого угла текущего ограничивающего прямоугольника:
  5. Введите значение x координаты угла в формулу линии (из 1) и вычтите значение y координаты из результата
  6. Запишите знак из расчета в 5
  7. Если все 4 знака равны, то столкновения не будет / не произойдет. Если какой-либо знак отличается, то возможно столкновение, выполните дальнейшие проверки.

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

Я нашел ссылку, которая объясняет это в сжатой форме, здесь.

Я не одарен математически, но я думаю, что вы могли бы сделать что-то вроде этого:

  1. Измерьте расстояние от центра блока и лазерного луча.
  2. Измерьте расстояние между центром блока и краем блока под заданным углом (была бы формула для этого, я просто не знаю, что это такое).

Вычтите результат из пункта 1 из результата из пункта 2.

Хорошо, что если точка 1 больше, чем точка 2, вы знаете, что столкновения еще не было.

В качестве альтернативы используйте box2d и просто используйте b2ContactPoint

Другие вопросы по тегам