views:

215

answers:

2

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

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

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.

SigTerm
Thanks for your answer SigTerm, I'll check if can get any of your ideas working.SigTerm: "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)"The 3rd dimension doesn't matter because the user including boxes are moving constantly on the ground anyway (i.e. the boxes are so big that the user won't be able to jump on them anyway)
ILikeGameProgramming
Oops I forgot to mention something, my problem is that there are also some objects called bricks which will block the user movement.
ILikeGameProgramming
@ILikeGameProgramming: For immovable objects simply never change move vector. I.e. for colliding objects A and B if B is immovable `A.move += pushAway;` and B.move remains unchanged. The rest is the same.
SigTerm
Actually I don't really understand that pushaway idea. For now I'm trying to move every movable thing the player hits, but I get an endless loop somehow.
ILikeGameProgramming
How can I actually calculate pushAway? Is that the overlapped area between the colliding objects or something?
ILikeGameProgramming
@ILikeGameProgramming: It is the shortest possible position change that would push object A away from object B so they are not overlapping. you could say it is penetration depth and direction. It is closely related to contact point calculation used in physics engine. For boxes you could assume that (process for each component - xyz) `pushAway.x = (A.position.x - B.position.x); pushAway.x = (A.size.x+B.size.x)*0.5 - pushAway.x`, then find which components of xyz have smallest absolute value and set other components (xyz) to zero - instead of pushAway(-1, 2, 1), you should get pushAway(-1, 0 1).
SigTerm
@ILikeGameProgramming: ...Which is why I recommended physics engine. Once upon a time I actually wrote a small set of routines that could calculate this "pushAway"(contact point, depth and normal) vector for many object combinations (axis-aligned box, oriented box, sphere, cylinder, capsule, plane), but it is pure math/geometry. Physics engine already have this stuff taken care of.
SigTerm
"pushAway.x = (A.position.x - B.position.x); pushAway.x = (A.size.x+B.size.x)*0.5 - pushAway.x"Why are you reassigning pushAway?
ILikeGameProgramming
Ow, I see, you reused it again
ILikeGameProgramming
Currently one of the objects is overlapping the other again when it moved which causes a continuous movement effect.How can I check which object (A or B) should be moved when collision is detected between them?
ILikeGameProgramming
@ILikeGameProgramming: "Currently one of the objects is overlapping the other again when it moved which causes a continuous movement effect." Probably because you forgot that for one object it is 0.5f*pushAway and for other it is -0.5f*pushAway. "How can I check which object (A or B) should be moved when collision is detected between them?" They both should be moved in opposite directions.
SigTerm
@ILikeGameProgramming: Look, I already wrote a small lecture here, and explaining this stuff isn't exactly interesting. After all, it is Q/A site, not a tutoring resource. At least try to draw overlapping boxes on paper and think how would YOU move them away so they don't collide, turn it into algorithm and implement it in program. You may want to get a physics book(or check chris hecker's rigid body articles), check the newton's laws to think how object mass comes into play - i.e. why larger box should be easier to move away than smaller one, and why I introduced mass in several formulas.
SigTerm
Thanks SigTerm, I already was started making drawings on paper yesterday (but it was I couldn't stay awake too long to finish it)."They both should be moved in opposite directions" No, I guess you got me wrong. I don't want them in opposite directions, but to move away from the player if they hit each other and the player. The player has already have an xplusser and zplusser which I use to move any hitted boxes.Anyway thanks for your help.
ILikeGameProgramming
I finally got a way to move the boxes. I added a new attribute to the box "totalPushes" so once the user hits it, it will increase. Then when checking for collision between the boxes them self, the box with the lowest totalPushes will move (from which also the totalPushes icreases) in every step of the loop.I only have a problem with immovable objects now. Somehow the boxes hesitate to return to their previous position while they were not colliding!
ILikeGameProgramming
A: 

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.

Dialecticus
Thanks Dialecticus, I think a search stack will solve the problem.
ILikeGameProgramming