2D Платформер Обработка столкновений - PullRequest
4 голосов
/ 01 марта 2011

Я пытаюсь создать 2D-платформерную игру (типа Марио), и у меня есть некоторые проблемы с правильной обработкой столкновений. Я пишу эту игру на C ++, используя SDL для ввода, загрузки изображений, загрузки шрифтов и так далее. Я также использую OpenGL через библиотеку FreeGLUT в сочетании с SDL для отображения графики.

Мой метод обнаружения столкновений - AABB (Axis-Aligned Bounding Box), и это действительно все, с чего мне нужно начать. Что мне нужно, так это простой способ определить, на какой стороне произошло столкновение, и правильно обработать его. Таким образом, в основном, если игрок сталкивается с вершиной платформы, переместите его на вершину; если происходит столкновение по сторонам, переместите игрока обратно на сторону объекта; если столкновение снизу, переместите игрока под платформу.

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

Если кто-то считает, что может помочь, ознакомьтесь с приведенным ниже кодом и, если возможно, помогите мне улучшить его. Я хотел бы воздержаться от использования библиотеки для обработки этого (как я хочу учиться самостоятельно) или чего-то подобного SAT (Теорема о разделяющей оси), если это вообще возможно. Заранее благодарю за помощь!

void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
    {
        de2dObj ballPrev;
        ballPrev.coords[0] = ball.coords[0];
        ballPrev.coords[1] = ball.coords[1];
        ballPrev.coords[2] = ball.coords[2];
        ballPrev.coords[3] = ball.coords[3];
        ballPrev.coords[0] -= ball.xspeed;
        ballPrev.coords[1] -= ball.yspeed;
        ballPrev.coords[2] -= ball.xspeed;
        ballPrev.coords[3] -= ball.yspeed;

        int up = 0;
        int left = 0;
        int right = 0;
        int down = 0;

        if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
        {
            left = 1;
        }

        if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
        {
            right = 1;
        }
        if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
        {
            up = 1;
        }
        if(block[i].coords[1] < ballPrev.coords[1] && ballPrev.coords[1] < block[i].coords[3] && block[i].coords[3] < ballPrev.coords[3])
        {
            down = 1;
        }

        cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;

        if (left == 1)
        {
            ball.coords[0] = block[i].coords[0] - 18.0f;
            ball.coords[2] = block[i].coords[0] - 2.0f;
        }
        else if (right == 1)
        {
            ball.coords[0] = block[i].coords[2] + 2.0f;
            ball.coords[2] = block[i].coords[2] + 18.0f;
        }
        else if (down == 1)
        {
            ball.coords[1] = block[i].coords[3] + 4.0f;
            ball.coords[3] = block[i].coords[3] + 20.0f;
        }
        else if (up == 1)
        {
            ball.yspeed = 0.0f;
            ball.gravity = 0.0f;
            ball.coords[1] = block[i].coords[1] - 17.0f;
            ball.coords[3] = block[i].coords[1] - 1.0f;
        }
    }
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
    {
        ball.gravity = -0.5f;
    }
}
}

Чтобы объяснить, что означает этот код:

Переменная блоков - это целое число, в котором хранится количество блоков или платформ. Я проверяю все блоки, используя цикл for, и номер, на котором в данный момент находится цикл, представлен целым числом i. Система координат может показаться немного странной, поэтому стоит объяснить. Координаты [0] представляют позицию х (слева) от объекта (где он начинается на оси х). Координаты [1] представляют позицию y (верхнюю часть) объекта (где он начинается на оси y). Координаты [2] представляют ширину объекта плюс координаты [0] (справа). Координаты [3] представляют высоту объекта плюс координаты [1] (внизу). de2dCheckCollision выполняет обнаружение коллизий AABB. Вверх отрицателен, а вниз положителен, как и в большинстве игр.

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

Еще раз спасибо за вашу помощь!

Редактировать 2 : я обновил свой код с помощью нового алгоритма, который проверяет, где мяч был ранее до столкновения. Угловые шкафы теперь работают на этой единственной платформе правильно, и когда у меня есть стена объектов, я могу правильно скользить по ней. Единственная оставшаяся проблема заключается в том, что возникает небольшой эффект дрожания, когда я на земле, когда шар постоянно поднимается и опускается, как будто его притягивает сила тяжести, а затем шар снова падает в объект.

Редактировать : Вот URL-адрес изображения, которое пытается показать, какие проблемы у меня возникают: http://img8.imageshack.us/img8/4603/collisionproblem.png

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

1 Ответ

12 голосов
/ 01 марта 2011

Одна проблема с вашим кодом состоит в том, что вы обнаруживаете только ситуации, подобные этой:

ball

Если круг оказывается полностью внутри блока, вы вообще не перемещаетесь. И это проблема.

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

Первое решение, которое приходит на ум, - это также посмотреть на последнюю позицию мяча; Точнее, посмотрите на дельта-вектор. Посмотрите, пересекает ли дельта-вектор стену. Если это так, измените положение в направлении, ориентированном по оси, к стене, пересекаемой дельта-вектором.

Редактировать: Когда я сказал «дельта-вектор», я забыл, что вы перемещаете квадрат, а не одну точку. Итак, если вы просто посмотрите на дельта-вектор верхнего левого угла, этого будет недостаточно, поскольку он может не обнаружить, что часть шара вошла в блок. Вместо этого вы можете посмотреть на дельта-векторы всех 4 углов.

...