Как переместить несколько прямоугольников в ответ на столкновение? - PullRequest
2 голосов
/ 28 июля 2010

Я пытаюсь создать игру (используя движок irrlicht с c ++), где вы можете поймать врага в ловушку с помощью ящиков. Но я просто не понимаю, как определить, что следует перемещать при обнаружении столкновения между пользователем и одним или несколькими блоками. Другое дело, что будут также некоторые объекты, называемые кирпичами, которые смогут блокировать движения.

Поскольку я не очень хорош в объяснении вещей, я включил изображение, поэтому оно, надеюсь, прояснит, что я имею в виду: альтернативный текст http://jrahmati.info/images/collision.gif

Я попробовал несколько вещей с моим кодом, но безуспешно. Поэтому я очень надеюсь, что кто-то предпримет попытку дать ответ на этот вопрос. Заранее спасибо. Кстати, мне не нужен ответ обязательно на языках c ++, java или .Net, тоже в порядке.

Для всех, кто интересуется кодом:

содержимое bool Game :: tryMove (user, dir) , которое пытается отодвинуться от игрока

bool thereIsCollision = false;
bool undoMovements = false;
bool userCollision = false;
do{
    thereIsCollision = false;
    for (int i = 0; i < totalObjects; i++) {
        //First check if object hits the user
        if(gameObjects[i].hits(user)){
            if (gameObjects[i]->isMovable()) {
                MovableObject* mObject = (MovableObject*) gameObjects[i];
                mObject->move(dir);
                mObject->setPushVector(dir);
                userCollision = true;
                //thereIsCollision = true;
            }
            else{
                undoMovements = true;
                thereIsCollision = false; //To break do-while loop
                userCollision = true;
                break;
            }
        }
    }
    if(undoMovements)
        break;
    for (int i = 0; i < totalObjects; i++) {
        //Then check if objects hit each other
        for (int i2 = 0; i2 < totalObjects; i2++) {
            if(i == i2)
                continue;
            if (gameObjects[i2].hits(gameObjects[i])){
               //thereIsCollision = true;
               if(gameObjects[i]->isMovable() && gameObjects[i2]->isMovable()){
                   MovableObject* mObject = (MovableObject*) gameObjects[i];
                   MovableObject* mObject2 = (MovableObject*) gameObjects[i2];
                   if(mObject->getPushVector().X > 0 
                           || mObject->getPushVector().Y > 0 
                           || mObject->getPushVector().Z > 0){
                       mObject2->move(mObject->getPushVector());
                       mObject2->setPushVector(mObject->getPushVector());
                       mObject->setPushVector(irr::core::vector3df(0, 0, 0));
                   }
                   else if(mObject2->getPushVector().X > 0 
                           || mObject2->getPushVector().Y > 0 
                           || mObject2->getPushVector().Z > 0){
                       mObject->move(mObject2->getPushVector());
                       mObject->setPushVector(mObject2->getPushVector());
                       mObject2->setPushVector(irr::core::vector3df(0, 0, 0));
                   }
               }
               else{
                   undoMovements = true;
                   thereIsCollision = false; //To break do-while loop
                   break;
               }
           }
        }
    }
}while(thereIsCollision);

for (int i = 0; i < totalObjects; i++) {
    if (gameObjects[i]->isMovable()) {
        MovableObject* mObject = (MovableObject*) gameObjects[i];
        if(undoMovements){
            // Resets position of gameObject to its previous one
            mObject->undoMovement();
        }
        else{
            // confirms movement(i.e. prevPosition=curPosition)
            mObject->confirmMovement();
        }
    }
}
return !(userCollision);

Ответы [ 2 ]

0 голосов
/ 31 июля 2010

Является ли это игрой в Сокобан, в которой игрок может двигаться в 8 направлениях и может перемещать несколько боксов одновременно? Видно ли игровое поле с высоты птичьего полета, как в Сокобане, или это падающие коробки, которые мы видим в примере (вид сбоку, как в тетрисе)? Является ли трение между ящиками и игроком бесконечным (иными словами, запрещено ли касание, в котором игрок касается, соскальзывать с направления движения игрока, когда он движется по диагонали)? Является ли трение между двумя коробками бесконечным?

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

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

0 голосов
/ 29 июля 2010

Насколько я понимаю, вы используете 2D или 3D-пространство, и ваши объекты не размещены на какой-то сетке (то есть, координаты плавающие). В этом случае есть 2 решения:

Решение № 1:

Использовать физический движок, например PhysX.

Решение № 2:

  1. Повторите 10 или 20 раз или пока не прекратятся столкновения:
    1.1 Найти все перекрывающиеся (сталкивающиеся) объекты. Это включает в себя игрока и коробки. Также ящики сталкиваются с другими ящиками.
    1.2. Для каждого объекта создайте вектор «перемещения» (на сколько объект должен двигаться в ответ на столкновение) m и инициализируйте его для всех нулей (x: 0, y: 0, z: 0).
    1.3. Для каждой пары сталкивающихся (перекрывающихся) объектов A и B рассчитайте вектор (скажем, «pushAwayVector»), который оттолкнет их друг от друга. Добавьте этот вектор к A.move и B.move. Имейте в виду, что объекты отталкиваются друг от друга, поэтому вы должны быть осторожны с признаками и величиной. то есть A.move += pushAway*0.5f; B.move += -0.5f*pushAway;. Не заменяйте .move вектором pushAway, но добавляйте pushAway для перемещения. В противном случае результаты будут менее надежными. Вы также можете принять во внимание массу объекта, например: A.move += (B.mass/(A.mass + B.mass))*pushAway; B.move += -(A.mass/(A.mass+B.mass))*pushAway;, в этом случае более легкому объекту будет сложнее толкнуть более тяжелый объект;
    1.4 Как только все перекрывающиеся объекты обработаны, для всех объектов выполните obj.position += obj.move;

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

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

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

Я бы порекомендовал использовать Physics Engine.

...