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

发布于 2024-09-12 04:23:43 字数 3584 浏览 7 评论 0原文

我正在尝试制作一个游戏(使用 irrlicht 引擎和 c++),您可以使用盒子来困住敌人。但我只是不知道当检测到用户与一个或多个盒子之间发生碰撞时如何检测应该移动的内容。另一件事是,还会有一些称为砖块的物体能够阻止运动。

由于我不太擅长解释事情,所以我添加了一张图片,因此它有望澄清我的意思: 替代文本
(来源: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);

I'm trying to make a game (using irrlicht engine with c++) where you can trap your enemy using boxes. But I just don't get how to detect what should be moved when a collision between the user and one or more boxes is detected. Another thing is that there will also be some objects called bricks which will be able to block movements.

Since I'm not very good in explaining things, I included an image, so it will hopefully clarify what I mean: alt text
(source: jrahmati.info)

I tried several things with my code but without success. So I really hope someone will take the effort to give an answer to this issue. Thanks in advance.
By the way, I don't need the answer necessarily in c++, java or .Net languages are also ok.

For anyone interested in the code:

content of bool Game::tryMove(user, dir) which tries to move everthing away from the player

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);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

慵挽 2024-09-19 04:23:44

据我了解,您使用的是 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;。不要用pushAway向量替换.move,而是添加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;

请注意,在每种情况下,不能保证框保持精确的相对位置。但当玩家移动时它们会被推开,并且会阻挡玩家的移动。

请注意,使用物理引擎将产生更接近您想要的结果。

另一个解决方案是一旦玩家触摸一个盒子,找到一组相互碰撞以及与玩家碰撞的盒子,然后删除不会受到当前玩家移动影响的盒子。然而,我不喜欢这个解决方案 - 它很混乱,缺乏优雅,尽管它会保持相对的对象位置(这也看起来不切实际)。

我建议使用物理引擎。

As I understand it, you're using 2d or 3d space, and your objects aren't placed on a some kind of grid (i.e. coordinates are floats). In this case there are 2 solutions:

Solution #1:

Use Physics engine, such as PhysX.

Solution #2:

  1. Repeat 10 or 20 times or until there are no more collisions:
    1.1 Find all overlapping (colliding) objects. That includes player and boxes. Also boxes colliding with other boxes.
    1.2 For each object create "move" vector (how much object should move in response to collision)m and initilize it to all zeroes (x: 0, y: 0, z: 0).
    1.3 For each pair of colliding(overlapping) objects A and B, calculate vector(say "pushAwayVector") that would push them away from each other. Add that vector to A.move and B.move. Keep in mind that objects are pushed away to each other, so you should be careful about signs and magnitude. i.e. A.move += pushAway*0.5f; B.move += -0.5f*pushAway;. Do not replace .move with pushAway vector, but add pushAway to move. Otherwise results will be less reliable. You can also take object's mass into account, like this: A.move += (B.mass/(A.mass + B.mass))*pushAway; B.move += -(A.mass/(A.mass+B.mass))*pushAway;, in this case it will be more difficult for lighter object to push heavier object;
    1.4 Once all overlapping objects are processed, for all objects do obj.position += obj.move;

Note that in each case boxes are not guaranteed to maintain exact relative positions. But they will be pushed away when player moves, and they will block player's movement.

Note that using physics engine will yield results that are closer to what you want.

Another solution is once player touches a box, find group of boxes that collide to each other and a player, then remove boxes that wouldn't be affected by current player movement. However, I do not like that solution - it is messy, lacks elegance, although it will maintain relative object positions (which will also look unrealistic).

I'd recommend using Physics Engine.

一张白纸 2024-09-19 04:23:44

这是一种推箱子游戏吗?玩家可以向 8 个方向移动,并且可以一次移动多个盒子?游戏场是像推箱子一样从鸟瞰角度看的,还是我们在示例中看到的掉落的盒子(侧视图,像俄罗斯方块)?盒子和玩家之间的摩擦力是否是无限的(换句话说,当玩家触摸的盒子沿对角线移动时,是否禁止其滑离玩家移动的方向)?两个盒子之间的摩擦力是无穷大吗?

我无法专注于你的代码,但这是我的 2cents 算法。该算法意味着任何两个物体之间的摩擦力是无限的。

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

Is this a sort of Sokoban game where the player can move in 8 directions, and is able to move several boxes at once? Is the play field viewed from birds eye view like in Sokoban, or are they falling boxes that we see in the example (side view, like in Tetris)? Is the friction between boxes and player infinite (in another words, is it forbidden for the box that player is touching to slide away from direction the player is moving, when it moves diagonally)? Is the friction between two boxes infinite?

I couldn't focus on your code, but here's my 2cents algorithm. The algorithm implies that friction between any two objects is infinite.

When player moves you need to establish which movable objects may be affected. Put the ones the player is touching in the moving direction into some search stack, then for each item in the search stack repeat the search process by putting more objects into it. Finally you'll run out of movable objects. If at any point you stumble upon a non-movable object then the player can't move as well (because of infinite friction rule), and so nothing moves. If there are no non-movable objects involved then move all objects in the stack by one grid unit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文