Hello Guest

Collision problem

  • 12 Replies
  • 10648 Views
*

Offline ic5y

  • *
  • 19
Collision problem
« on: July 03, 2013, 21:09:06 »
So, I have a problem with collision response. I save the last good, not colliding position, and then, when the player collides with a wall, I set the position to the previous position. But when I run it, lastGoodPos is ALWAYS set to position. Even when there is collision.. any idea?

Code: [Select]
public void update()
{
        float forward = 0f;
        float strafe = 0f;
        float vertical = 0f;

if(Keyboard.isKeyDown(Keyboard.KEY_W))
{
            forward += 1f;
}
else if(Keyboard.isKeyDown(Keyboard.KEY_S))
{
            forward -= 1f;
}
else if(Keyboard.isKeyDown(Keyboard.KEY_A))
{
            strafe -= 1f;
}
else if(Keyboard.isKeyDown(Keyboard.KEY_D))
{
            strafe += 1f;
}
        else if(Keyboard.isKeyDown(Keyboard.KEY_Q) && freeCam)
        {
            vertical -= 1f;
        }
        else if(Keyboard.isKeyDown(Keyboard.KEY_E) && freeCam)
        {
            vertical += 1f;
        }

        int dx = Mouse.getDX();
        int dy = Mouse.getDY();

        yrot += dx * 0.5f;
        xrot -= dy * 0.5f;

        if(xrot > 90)
            xrot = 90;
        else if(xrot < -90)
            xrot = -90;

        GL11.glRotatef(xrot, 1, 0, 0);
        GL11.glRotatef(yrot, 0, 1, 0);

        forward *= Game.graphics.getDelta();
        strafe *= Game.graphics.getDelta();

        Vector3f movement = new Vector3f();

        if(strafe == 0f || forward != 0)
        {
            movement.x += SPEED * forward * Math.cos(Math.toRadians(yrot + 90));

            if(freeCam)
                movement.y += SPEED * vertical;
            else
                movement.y = -0.5f;

            movement.z += SPEED * forward * Math.sin(Math.toRadians(yrot + 90));
        }
        else
        {
            movement.x += SPEED * strafe * Math.cos(Math.toRadians(yrot + 180));

            if(freeCam)
                movement.y += SPEED * vertical;
            else
                movement.y = -0.5f;

            movement.z += SPEED * strafe * Math.sin(Math.toRadians(yrot + 180));

        }

        position.x -= movement.x;
        position.z -= movement.z;

        bb.setVecMin(new Vector3f(position.x - BBSIZE, 0f, position.z - BBSIZE));
        bb.setVecMax(new Vector3f(position.x + BBSIZE, 1f, position.z + BBSIZE));

        boolean c = false;

        for(BoundingBox b : Game.level.bbs)
        {
            if(BoundingBox.isCollided(bb, b))
            {
                c = true;
                break;
            }
        }

        if(c)
        {
            System.out.println("Collided!");
            position = lastGoodPos;
        }
        else
            lastGoodPos = position;

        GL11.glTranslatef(-position.x, -0.5f, -position.z);
}

*

Offline quew8

  • *****
  • 569
  • Because Square Eyes Look More Real
Re: Collision problem
« Reply #1 on: July 03, 2013, 22:09:31 »
Instances of objects (like position and lastGoodPos) in java are stored by reference. This means that when you say lastGoodPos = position, you are not setting the values stored in lastGoodPos to the values stored in position. Rather you are saying lastGoodPos IS position ie they are the same object. So when later on you start changing the values of position you are implicitly also changing the values of lastGoodPos. Which is causing you these problems.

I will let you find your own solution to this as it is a vital concept for java programmers to grasp.

*

Offline ic5y

  • *
  • 19
Re: Collision problem
« Reply #2 on: July 04, 2013, 12:32:59 »

*

Offline quew8

  • *****
  • 569
  • Because Square Eyes Look More Real
Re: Collision problem
« Reply #3 on: July 04, 2013, 13:09:09 »
I'm not familiar with LWJGL vector classes (For some reason I wrote my own and never used LWJGL's). However I expect the method they use is to arc-cosine the dot product of the two vectors (don't worry if you don't know what that means) for which it is necessary that both vectors be normalized. If not then stuff like what you say would happen.

This then leads me to ask if you sure you know what you're doing with the angle between these two vectors. As far as I can see, it makes no sense. This method will give you the angle between the two position vectors of these positions (a position vector is the vector from the origin to the position).

So in case you now have to rethink your method, I shall suggest another using a bit of pseudo-physics. (Please note I have to assume you know nothing about this so I may be a bit patronizing) The normal of a face is a vector perpendicular to the face. The normal reaction is the force a face applies to an object (parallel to the normal) when the object pushes on it. (Newtons third law - You push down on the ground, the ground pushes up on you). You can work out this normal reaction as a velocity (rather than a force) and if you add it to the objects velocity before actually moving it, then the object will slide along the face rather than being stopped dead.

To work out the normal reaction. (Very simple really, hard to get head around)
If an object as velocity V, and hits a face with normal N

Then Normal Reaction R = N * -( N . V )
( "." means dot product )

If you add this to the objects speed then you get a final velocity. So all together:

Final Velocity Vf = V + ( N * -( N . V ) )

Just be careful to normalize the NORMAL (keys in the name) but not to normalize the velocity.

*

Offline ic5y

  • *
  • 19
Re: Collision problem
« Reply #4 on: July 04, 2013, 18:44:46 »
Let's see if I understand it correctly..

First, I check if there was a collision. If there was any, I get the wall's normal, and calculate the new velocity from that formula, and then I add it to the position?

*

Offline quew8

  • *****
  • 569
  • Because Square Eyes Look More Real
Re: Collision problem
« Reply #5 on: July 04, 2013, 21:52:55 »
You got it. Probably best to store the normal of the face as it can be quite expensive to calculate. You perhaps have it anyway for lighting / collision detection purposes. But otherwise that's it exactly.

*

Offline ic5y

  • *
  • 19
Re: Collision problem
« Reply #6 on: July 05, 2013, 12:18:06 »
EDIT: my normals were wrong, got it perfectly! Thank you!

EDIT2: Hmm.. the player is "flickering" when colliding with the wall, but the sliding thing is working. How could I smoothen that movement? Especially on lower FPS... I tried multiplying with delta, and even dividing by it, no success :/

Code: [Select]
if(c != null)
        {
            Vector3f reaction = new Vector3f();
            reaction.x = c.normal.x * - (Vector3f.dot(c.normal, movement));
            reaction.y = c.normal.y * - (Vector3f.dot(c.normal, movement));
            reaction.z = c.normal.z * - (Vector3f.dot(c.normal, movement));

            Vector3f finalV = new Vector3f();

            Vector3f.add(movement, reaction, finalV);

            Vector3f.add(finalV, lastGoodPos, position);

}
« Last Edit: July 05, 2013, 14:11:54 by ic5y »

*

Offline quew8

  • *****
  • 569
  • Because Square Eyes Look More Real
Re: Collision problem
« Reply #7 on: July 05, 2013, 16:18:32 »
OK, I have to admit I forgot about multiplying by delta.

What I meant was that final velocity should actually replace the initial velocity. What you have now is that each frame the object ties to move into the wall and each frame gets rebuffed off. Repetitive actions like this tend to cause flickering/juddering effects. Not entirely sure why but I recon its due to the varying values of delta each frame.

In short, change
Code: [Select]
Vector3f finalV = new Vector3f();

Vector3f.add(movement, reaction, finalV);

Vector3f.add(finalV, lastGoodPos, position);

to

Code: [Select]
Vector3f.add(movement, reaction, movement);

Vector3f.add(movement, lastGoodPos, position);

*

Offline ic5y

  • *
  • 19
Re: Collision problem
« Reply #8 on: July 07, 2013, 15:13:31 »
I uploaded my code to pastebin, because it's too long to post here: link

Still flickering... I have no idea... I guess the step that the player takes in one frame is too big


*

Offline quew8

  • *****
  • 569
  • Because Square Eyes Look More Real
Re: Collision problem
« Reply #9 on: July 07, 2013, 15:49:21 »
Two things I notice from that code:

1) You currently have:
Quote
reaction.x = c.normal.x * + (Vector3f.dot(c.normal, movement));
Unless you are actually storing the negative of the normal (or something like that) shouldn't you have
...c.normal.x * -(Vector3f.dot...

2) I get the impression that your bounding boxes are in fact axis-aligned cuboid type things. If this is correct, they should have 6 faces each with its own normal. I wonder how you're getting the right normal from just:
Quote
...c.normal...

*

Offline ic5y

  • *
  • 19
Re: Collision problem
« Reply #10 on: July 07, 2013, 16:15:38 »
I currently not calculating the normal, I just hardcoded them, it looked easier for me, as each AABB is "just a plane", so e.g. a wall with a facing of north, has 0 depth, so the AABB's minimum vector's Z and the maximum's Z is equal. I add the dot value because, maybe from the bad normals, the player move inverted on the X and the Z axis, that's why I added the value.

*

Offline ic5y

  • *
  • 19
Re: Collision problem
« Reply #11 on: July 08, 2013, 17:41:33 »
Interesting, I solved it.. Yes, the reaction should be ...* - (Vector3f.dot(c.normal, m..., but then I had to add the inverse of the movement vector. I don't know why, but now works like a charm. Thank you

*

Offline quew8

  • *****
  • 569
  • Because Square Eyes Look More Real
Re: Collision problem
« Reply #12 on: July 18, 2013, 08:58:44 »
In reply to pm:

If your bounding boxes are axis-aligned then, by definition, the normal must be facing along one of the axes and in this case, the best option is to simply tell it what axis it points along (bearing in mind it can face forward or backwards) when you create it.

If not axis-aligned, then we need some maths. Given 3 vertices of the face in order V1, V2, V3.
 
    The Normal, N = ( V1 - V2 ) X ( V3 - V2 )

where X is the cross product (aka vector product). However, it is highly unlikely that this normal will be normalized, so you shall have to do it.

The final problem is which way it faces, since this could give you the correct forward facing vector, or the negation of it facing backwards. Which it gives you depends on the order you give the vertices (there is some kind of left/right hand rule for this but I can't remember if it is the left or the right hand). Since your vertices should always be wound counter-clockwise for OpenGL (unless you changed the setting) you don't have to test this on a per-face basis. Set it one way, try it, and if they are facing the  wrong way, swap around V1 and V3 in the sum. Hope this helps.