2D-реакция на столкновение

Я искал на форумах, и я не смог найти ничего достаточно конкретного для того, с чем у меня возникли проблемы. Я пытаюсь создать функцию для обнаружения столкновений в 2D-платформере, и сначала я мог заставить ее работать с явным указанием каждой вершины и того места, куда игрок затем перемещался бы при столкновении, но это было совсем неэффективно и закончилось с большим количеством перепечатывания вещей. Сначала это будет работать, но со временем станет больше хлопот, чем того стоит. Я пытаюсь создать функцию обнаружения столкновений, которая сможет сказать мне, с какой стороны сталкивается игрок, и сможет переместить персонажа в нужное положение. Я использую allegro 5 и С++, и это моя функция до сих пор:

bool Collision(int x1,int y1,int h1,int w1,int x2,int y2,int h2,int w2){

    if(x1 < x2 + w2 &&
       x2 < x1 + w1 &&
       y1 < y2 + h2 &&
       y2 < y1 + h1)
       {return 1;}

    return 0;
}

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


person Dr. President    schedule 03.10.2014    source источник
comment
Какой у Вас вопрос?   -  person Code-Apprentice    schedule 04.10.2014
comment
как я могу сделать так, чтобы при столкновении персонаж останавливался и не продолжал движение через объект   -  person Dr. President    schedule 04.10.2014
comment
@Доктор Президент if (Collision(x1,y1,h1,w1,x2,y2,h2,w2)) StopAndNotContinueThroughTheObject(); ?   -  person Marlon    schedule 04.10.2014
comment
но как это реализовать? если я просто установлю движение на 0, оно остановится там, где оно не обязательно находится на краю, оно может быть в пределах границ объекта   -  person Dr. President    schedule 04.10.2014


Ответы (2)


ПОВТОРНО ОТРЕДАКТИРОВАНО Если вы хотите, чтобы объект останавливался НЕПОСРЕДСТВЕННО ПЕРЕД фактическим столкновением, не разделяя один и тот же фактический пиксель края, попробуйте следующее:

bool Collision(int x1,int y1,int h1,int w1,int x2,int y2,int h2,int w2){

    if((x1 + w1) >= (x2 - 1)  ||    // object 1 hitting left side of object 2
       (x1 - 1)  <= (x2 + w2) ||    // object 1 hitting right side of object 2
       (y1 - 1)  <= (y2 + h2) ||    // Object 1 hitting bottom of object 2 (assuming your y goes from top to bottom of screen)
       (y1 + h1) >= (y2 - 1))      // Object 1 hitting top of object 2
        return 1;

    return 0;
}

ИЛИ

int Collision(int x1,int y1,int h1,int w1,int x2,int y2,int h2,int w2){

    if((x1 + w1) >= (x2 - 1))  return 1;    // object 1 hitting left side of object 2
    if((x1 - 1)  <= (x2 + w2)) return 2;    // object 1 hitting right side of object 2
    if((y1 - 1)  <= (y2 + h2)) return 3;    // Object 1 hitting bottom of object 2 (assuming your y goes from top to bottom of screen)
    if((y1 + h1) >= (y2 - 1))  return 4;    // Object 1 hitting top of object 2

    return 0;  // no collision
}

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

ОРИГИНАЛ Я думаю, что вы хотите пойти дальше:

bool Collision(int x1,int y1,int h1,int w1,int x2,int y2,int h2,int w2){

    if((x1 + w1) >= x2 ||    // object 1 hitting left side of object 2
       x1 <= (x2 + w2) ||    // object 1 hitting right side of object 2
       y1 <= (y2 + h2) ||    // Object 1 hitting bottom of object 2 (assuming your y goes from top to bottom of screen)
       (y1 + h1) >= y2)      // Object 1 hitting top of object 2
        return 1;

    return 0;
}

Этот ответ предполагает, что вы хотите знать, когда два объекта не повреждены, ЗАНИМАЮТ одно и то же координатное ребро (т.е. меньше/больше или равно по сравнению с без равенства)

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

int Collision(int x1,int y1,int h1,int w1,int x2,int y2,int h2,int w2){

    if((x1 + w1) >= x2) return 1;    // object 1 hitting left side of object 2
    if(x1 <= (x2 + w2)) return 2;    // object 1 hitting right side of object 2
    if(y1 <= (y2 + h2)) return 3;    // Object 1 hitting bottom of object 2 (assuming your y goes from top to bottom of screen)
    if((y1 + h1) >= y2) return 4;    // Object 1 hitting top of object 2

    return 0;  // no collision
}

Теперь снаружи вам просто нужно декодировать случаи обнаружения краев 1-4.

person trumpetlicks    schedule 03.10.2014
comment
хорошо, спасибо, я попробую, я не думал, что попробую так - person Dr. President; 04.10.2014
comment
но разве это не скажет мне, что значения x пересекаются? не фактическое столкновение, потому что оно не должно быть в пределах границ, оно может быть просто выше или рядом с ним? - person Dr. President; 04.10.2014
comment
Я думаю, вам нужно лучше определить, что вы действительно хотите, чтобы столкновение было для вашего приложения. Вы можете изменить приведенный выше код, чтобы он останавливался ТОЛЬКО перед этим, добавив модификатор +-1 к позиции второго объекта. - person trumpetlicks; 04.10.2014

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

bool Collision(GameObject o1, GameObject o2){
  return !(o1.x + o1.w < o2.x || o1.y + o1.h < o2.y || o1.x > o2.x + o2.w || o1.y > o2.y + o2.h);
}

void moveThisObject(GameObject &movingObject) {
    bool redoMovement;                

    // Move and test x-axis movement caused collision.
    movingObject.x += movingObject.speed.x;         
    redoMovement=false;
    for (int t=t;t<objectsInGame;t++) {
            if (Collision(movingObject,gameObj[t])
                    redoMovement=true;
    }
    if (redoMovement)
            movingObject.x -= movingObject.speed.x;

    // Move and test y-axis movement caused collision.
    movingObject.y += movingObject.speed.y;         
    redoMovement=false;
    for (int t=t;t<objectsInGame;t++) {
            if (Collision(movingObject,gameObj[t])
                    redoMovement=true;
    }
    if (redoMovement)
            movingObject.y -= movingObject.speed.y;
} 
class GameObject {
    public:
        double x,y,w,h;
        Vec2 speed;
}
class Vec2 {
    public:
        double x,y;
}

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

// for x-axis testing
if (redoMovement) {
    if (movingObject.speed.x>0) //moving object hit object2 from left to right
        movingObject.x -= 1; // move back left
    if (movingObject.speed.x<0) //moving object hit object2 from right to left
        movingObject.x += 1; // move back right
}
// for y-axis testing
if (redoMovement) {
    if (movingObject.speed.y>0) //moving object hit object2 from up to down
        movingObject.y -= 1; // move back down
    if (movingObject.speed.y<0) //moving object hit object2 from down to up
        movingObject.y += 1; // move back   up
}

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

person tonis    schedule 17.10.2014