如何将多个矩形作为碰撞响应一起移动?

3
我正在尝试制作一款游戏(使用C++的irrlicht引擎),在这个游戏中,你可以使用箱子来困住你的敌人。但是我不知道如何检测当用户与一个或多个箱子发生碰撞时应该移动什么。另一件事是,还将有一些名为砖块的物体,它们将能够阻止移动。
由于我不太善于解释事情,所以我包含了一张图片,希望能够澄清我的意思:alt text (来源:jrahmati.info 我已经尝试了几种方法,但都没有成功。所以我真的希望有人能够花费精力回答这个问题。提前感谢。 顺便说一下,我不一定需要用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);

我认为你提供了太多的代码,把每个人都吓跑了。 - Brian
我猜代码甚至不是必要的,因为我只想了解如何做到这一点的想法。 - J. Rahmati
2个回答

1
据我所知,您正在使用2D或3D空间,并且您的对象没有放置在某种网格上(即坐标为浮点数)。在这种情况下,有两个解决方案:

解决方案#1:

使用物理引擎,例如PhysX。

解决方案#2:

  1. 重复10或20次,或直到没有碰撞为止:
    1.1 找到所有重叠(碰撞)的对象。这包括玩家和箱子。还有与其他箱子碰撞的箱子。
    1.2 对于每个对象创建“移动”向量(对象应该对碰撞做出多少移动),并将其初始化为全零(x:0、y:0、z:0)。
    1.3 对于每一对重叠(碰撞)对象A和B,计算一个向量(称为“远离向量”),使它们彼此远离。将该向量添加到A.move和B.move中。请记住,对象是互相推开的,因此您应该小心符号和大小。例如:A.move += pushAway*0.5f; B.move += -0.5f*pushAway;不要用pushAway向量替换.move,而是将pushAway添加到move中。否则结果将不太可靠。您还可以考虑对象的质量,如下所示:A.move += (B.mass /(A.mass + B.mass))*pushAway; B.move += -(A.mass /(A.mass + B.mass))*pushAway;在这种情况下,较轻的对象要推动较重的对象更加困难;
    1.4 处理完所有重叠对象后,对于所有对象执行obj.position += obj.move;
请注意,在每种情况下,不能保证盒子的确切相对位置。但是当玩家移动时,它们将被推开,并阻止玩家的移动。
请注意,使用物理引擎将产生更接近您想要的结果。
另一种解决方案是,一旦玩家触摸到一个盒子,找到彼此碰撞的一组盒子和玩家,然后删除不会受到当前玩家移动影响的盒子。然而,我不喜欢那个解决方案-它很混乱,缺乏优雅,尽管它将保持相对对象位置(这也看起来不真实)。
我建议使用物理引擎。

谢谢你的回答SigTerm,我会看看能否实现你的想法。SigTerm:“据我所知,您正在使用2D或3D空间,并且您的对象没有放置在某种网格上(即坐标为浮点数)”第三个维度并不重要,因为用户包括盒子在内都在地面上不断移动(即盒子太大了,用户无法跳上它们)。 - J. Rahmati
哎呀,我忘了提到一件事,我的问题是还有一些叫做砖块的物体会阻碍用户移动。 - J. Rahmati
@ILikeGameProgramming:对于不可移动的物体,只需永远不改变移动向量即可。例如,对于碰撞物体A和B,如果B是不可移动的,则A.move += pushAway;而B.move保持不变。其余部分相同。 - SigTerm
其实我并不太理解那个推开的概念。目前我正在尝试移动玩家触碰到的所有可移动物体,但是不知道为什么会出现无限循环。 - J. Rahmati
我应该如何计算pushAway?它是碰撞物体之间的重叠面积还是什么? - J. Rahmati
显示剩余9条评论

0

这是一种类似推箱子游戏的玩法吗?玩家可以朝8个方向移动,并且能够同时移动多个箱子吗?游戏场景是否像推箱子一样俯视图,或者像示例中看到的掉落箱子(侧视图,就像俄罗斯方块)?箱子和玩家之间的摩擦力是否无限大(换句话说,当它在对角线移动时,玩家触碰的箱子是否被禁止从玩家移动的方向滑动)?两个箱子之间的摩擦力是否无限大?

我无法专注于您的代码,但这是我的算法建议。该算法意味着任何两个物体之间的摩擦力都是无限大的。

当玩家移动时,您需要确定哪些可移动对象可能会受到影响。将玩家朝着移动方向触碰到的物体放入某个“搜索堆栈”中,然后对于搜索堆栈中的每个项目,通过将更多对象放入其中来重复搜索过程。最终,您将用完所有可移动对象。如果在任何时候遇到不可移动的对象,则玩家也无法移动(因为有无限摩擦规则),因此没有任何物体移动。如果没有涉及到不可移动的对象,则将堆栈中的所有对象移动一个网格单位。


谢谢Dialecticus,我认为一个搜索栈会解决这个问题。 - J. Rahmati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接