Спорадическое обнаружение столкновений - PullRequest
4 голосов
/ 24 марта 2011

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

Сейчас я зацикливаюсь на каждом объекте, проверяя наличие столкновений:

for(std::list<Object*>::iterator iter = mObjectList.begin(); iter != mObjectList.end();) {
    Object *m = (*iter);
    for(std::list<Object*>::iterator innerIter = ++iter; innerIter != mObjectList.end(); innerIter++ ) {
            Object *s = (*innerIter);

            if(m->getType() == s->getType()) {
                break;
            }

            if(m->checkCollision(s)) {
                m->onCollision(s);
                s->onCollision(m);
            }
        }
    }

Вот как я проверяю столкновение:

bool checkCollision(Object *other) {
        float radius = mDiameter / 2.f;
        float theirRadius = other->getDiameter() / 2.f;
        Vector<float> ourMidPoint = getAbsoluteMidPoint();
        Vector<float> theirMidPoint = other->getAbsoluteMidPoint();

        // If the other object is in between our path on the y axis
        if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
            theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius)) {

                // Get the distance between the midpoints on the x axis
                float xd = abs(ourMidPoint.x - theirMidPoint.x);

                // If the distance between the two midpoints
                // is greater than both of their radii together
                // then they are too far away to collide
                if(xd > radius+theirRadius) {
                    return false;
                } else {
                    return true;
                }

        }
        return false;
}

Проблема в том, что он случайным образом правильно обнаруживает столкновения, но в других случаях он вообще не обнаруживается.Это не оператор if, выходящий из цикла объектов, потому что объекты имеют разные типы.Чем ближе объект находится к верхней части экрана, тем выше вероятность того, что столкновение будет правильно обнаружено.Чем ближе к нижней части экрана, тем меньше у него шансов на то, чтобы его правильно или вообще не обнаружили.Однако такие ситуации не всегда случаются.Диаметр объектов огромен (10 и 20), чтобы понять, в этом ли проблема, но это мало помогает.

РЕДАКТИРОВАТЬ - Обновлен код

bool checkCollision(Object *other) {
    float radius = mDiameter / 2.f;
    float theirRadius = other->getDiameter() / 2.f;
    Vector<float> ourMidPoint = getAbsoluteMidPoint();
    Vector<float> theirMidPoint = other->getAbsoluteMidPoint();

    // Find the distance between the two points from the center of the object
    float a = theirMidPoint.x - ourMidPoint.x;
    float b = theirMidPoint.y - ourMidPoint.y;

    // Find the hypotenues
    double c = (a*a)+(b*b);
    double radii = pow(radius+theirRadius, 2.f);

    // If the distance between the points is less than or equal to the radius
    // then the circles intersect
    if(c <= radii*radii) {
        return true;
    } else { 
        return false;
    }
}

Ответы [ 3 ]

3 голосов
/ 24 марта 2011

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

double distanceSquared =
    pow(ourMidPoint.x - theirMidPoint.x, 2.0) +
    pow(ourMidPoint.x - theirMidPoint.x, 2.0);
bool haveCollided = (distanceSquared <= pow(radius + theirRadius, 2.0));

Чтобы проверить, было ли столкновение между двумя моментами времени, вы можете проверить наличие столкновений в начале временного интервала и в концеэтого;однако, если объекты движутся очень быстро, обнаружение столкновения может завершиться неудачей (я полагаю, вы столкнулись с этой проблемой для падающих объектов, которые имеют самую высокую скорость в нижней части экрана).обнаружение более надежно (хотя все еще не идеально).Предположим, что объекты движутся с постоянной скоростью;затем их положение является линейной функцией времени:

our_x(t) = our_x0 + our_vx * t;
our_y(t) = our_y0 + our_vy * t;
their_x(t) = their_x0 + their_vx * t;
their_y(t) = their_y0 + their_vy * t;

Теперь вы можете определить (квадрат) расстояние между ними как квадратичную функцию времени.Найти, в какое время он принимает минимальное значение (т.е. его производная равна 0);если это время относится к текущему временному интервалу, рассчитайте минимальное значение и проверьте его на наличие коллизий.

Этого должно быть достаточно для почти полного обнаружения коллизий;если ваше приложение интенсивно работает со свободно падающими объектами, вам может потребоваться улучшить квадратичность функций движения:

our_x(t) = our_x0 + our_v0x * t;
our_y(t) = our_y0 + our_v0y * t + g/2 * t^2;
2 голосов
/ 24 марта 2011

Эта логика неверна:

if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
        theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius))
{
  // then a collision is possible, check x
}

(Логика внутри фигурных скобок тоже неверна, но это должно давать ложные срабатывания, а не ложные отрицания.) Проверка, не произошло ли столкновение в течение определенного промежутка времени; Я бы посоветовал проверить наличие столкновения в настоящее время и сначала заставить его работать. Когда вы проверяете столкновение (сейчас), вы не можете проверить x и y независимо, вы должны посмотреть на расстояние между центрами объектов.

EDIT:

Отредактированный код все еще не совсем верен.

// Find the hypotenues
double c = (a*a)+(b*b); // actual hypotenuse squared
double radii = pow(radius+theirRadius, 2.f); // critical hypotenuse squared

if(c <= radii*radii) { // now you compare a distance^2 to a distance^4
    return true; // collision
}

Это должно быть либо:

double c2 = (a*a)+(b*b); // actual hypotenuse squared
double r2 = pow(radius+theirRadius, 2.f); // critical hypotenuse squared

if(c2 <= r2) {
    return true; // collision

}

или это:

double c2 = (a*a)+(b*b); // actual hypotenuse squared
double c = pow(c2, 0.5); // actual hypotenuse
double r = radius + theirRadius; // critical hypotenuse

if(c <= r) {
    return true; // collision

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

Ваш внутренний цикл должен начинаться с mObjectList.begin () вместо iter.

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

...