Collision response with velocity not correct with rectangle corners?

Started by
4 comments, last by Spa8nky 14 years, 2 months ago
Hi folks, If I have two rectangles, one static and the other controllable, I can resolve an intersection between the two using: rectangle_Moving.Centre += contact.normal * contact.penetration; If I decide to create a particle that lies at the centre of the moving rectangle and update the position of the rectangle based on the particle's position, then the collision resolution steps I take are as follows: - Integrate particle - Set moving rectangle centre to particle's current position - Test for intersection [If intersecting]: - Adjust rectangle position (as shown above) - Adjust particle velocity

                // Update the particle
                debug_Particle.Integrate(dt_Seconds);

                // Set the rectangle's centre to the particle position
                debug_Rectangle[0].Centre = debug_Particle.Position;

                if (Detection2D.TestRectangleRectangle(debug_Rectangle[0], debug_Rectangle[1], ref contact))
                {
                    // Correct the intersection
                    debug_Rectangle[0].Centre += contact.normal * contact.penetration;

                    // Correct the velocity
                    // Sliding response (remove all components of velocity along the normal of collision)
                    float vDotN = Vector2.Dot(debug_Particle.Velocity, contact.normal);
                    debug_Particle.Velocity -= contact.normal * vDotN;
                }


Unfortunately the collision resolution doesn't work as planned and the moving rectangle will stick to the static rectangles corners. Can anyone please tell me what I may have done wrong here please? Thank you.
Advertisement
Have you made sure that the contact.normal is always perpendicular to only one of the static rectangle's sides? (And that the contact.penetration is along that vector)
- Because if the normal would be diagonal (e.g. on a corner) it could cause getting stuck, when both the horizontal and vertical velocity may be canceled by the corresponding diagonal correction.
The rectangles never get stuck when just adjusting position, only when adjusting velocity.

The collision normals are only ever perpendicular to the rectangle sides and are never diagonal for the corners.

I am integrating my particle as follows:

        public void Integrate(float duration)        {            // We don't integrate things with zero mass             // <= ??!??!?! 0 = infinite mass?!?!?!?!?!?!?!?!            if (mass_Inverse <= 0.0f)            {                return;            }            // ==========================================================================================================            // • Work out the acceleration from all the forces acting on the particle             // • Forces are stored in force_Accumulated            for (int i = 0; i < force_Generators.Count; i++)            {                force_Generators.UpdateForce(this, duration);            }            // ==========================================================================================================            // Update acceleration from accumulated forces            Vector2 acceleration_Resulting = acceleration;            acceleration_Resulting += force_Accumulated;            // Update linear velocity from the acceleration            velocity += acceleration_Resulting * duration;            // Update linear position            position += velocity * duration;            // Clear the accumulated forces            ClearAccumulatedForce();        }


Maybe that is where I am going wrong?
I don't see anything causing sticking there, the velocity is accumulated and should keep any sliding. Especially if you have 0 force.

I presume the particle is completely still when you describe it as stuck, and that there is nothing that keeps driving/accelerating the particle after a first collision-correction.

If you can make a debugger breakpoint when getting stuck on a corner try to confirm that the velocity really gets parallel to the static rect-side (to see whether first code snippet works), then step until next loop update and ensure that the same velocity remains "sliding" the same way - if it does not that indicates the velocity has been lost somewhere else, which it shouldn't.

Such a loss can possibly be caused by copying only part of the particle data, or modifying a temporary instance instead of the real one, or another code part interrupting (the debugger can help find that). Personally I use to find "data breakpoint" useful when a variable is correct at one point but gets wrong at an unknown point.
Quote:
I presume the particle is completely still when you describe it as stuck, and that there is nothing that keeps driving/accelerating the particle after a first collision-correction.


The particle velocity is (0,0) when it is "stuck" at the corner. If the particle is traveling in the +ve y direction the y velocity remains at 0 when sliding and never changes.

Maybe the problem might be that the collision detection is returning the wrong axis when the rectangles are parallel?

Can anyone please confirm/deny that this might be the case?

        public static bool TestRectangleRectangle(Collidable2D col_A, Collidable2D col_B, ref Contact2D contact)        {            CD_Rectangle a = (CD_Rectangle)col_A;            CD_Rectangle b = (CD_Rectangle)col_B;            // [Separating Axis Theorem]            // • Two convex shapes only overlap if they overlap on all axes of separation            // • In order to create accurate responses we need to find the collision vector (Minimum Translation Vector)               // • Find if the two boxes intersect along a single axis             // • Compute the intersection interval for that axis            // • Keep the smallest intersection/penetration value            // Minimum Translation Vector parameters            float mtv_Distance = float.MaxValue;            // Set current minimum distance (max float value so next value is always less)            int mtv_Axis = 0;                               // Axis along which to travel with the minimum distance            // For each separating axis            for (int i = 0; i < 2; i++)            {                // Find distance intervals for current two slabs                // Distance is between slab min/max values                float d_S0 = a.MinPoint.Index(i) - b.MaxPoint.Index(i);                float d_S1 = b.MinPoint.Index(i) - a.MaxPoint.Index(i);                // The distance is positive if the intervals do not overlap                if (d_S0 > 0f || d_S1 > 0f)                {                    return false;                }                // Current distance interval for slabs                float d = (d_S0 > d_S1) ? -d_S0 : d_S1;                // If d is the smallest distance so far                if (Math.Abs(d) < Math.Abs(mtv_Distance))                {                    // Store the distance and the current axis                    mtv_Distance = d;                    mtv_Axis = i;                }            }            // Minimum Translation Vector            Vector2 mtv = Vector2.Zero;            switch (mtv_Axis)            {                case 0:                    mtv.X = mtv_Distance;                    break;                case 1:                    mtv.Y = mtv_Distance;                    break;            }            contact.normal = Vector2.Normalize(mtv);            contact.penetration = Math.Abs(mtv_Distance) + Constants.EPSILON;            return true;        }


Do I need to change the above method to avoid incorrect collision detection when the rectangles are parallel?

Thank you.
I have discovered what I think is now the true source of the problem.

When I push the particle in a +y direction using the following:

    class Particle2D_UserInput : IParticle2D_ForceGenerator    {        public Particle2D_UserInput() { }        public override void UpdateForce(Particle2D particle, float duration)        {            Vector2 force = Vector2.Zero;            // Get user input state            if (InputState.Global.IsKeyDown(PlayerIndex.One, Keys.Up))            {                force += Vector2.UnitY;            }            if (InputState.Global.IsKeyDown(PlayerIndex.One, Keys.Down))            {                force += -Vector2.UnitY;            }            if (InputState.Global.IsKeyDown(PlayerIndex.One, Keys.Left))            {                force += -Vector2.UnitX;            }            if (InputState.Global.IsKeyDown(PlayerIndex.One, Keys.Right))            {                force += Vector2.UnitX;            }            particle.AddForce(force);        }    }


The particle's velocity will be 0 in the push direction upon collision, which is correct. However, the particle's position will slowly push into the +y direction even when a collision is detected.

This can be resolved using:

debug_Particle.Position -= contact.normal * contact.penetration;

but this is not a good way of applying physics, correct? I should be applying velocities to the particle!

Can anyone explain why the particle position keeps moving in +y direction, even when the velocity in that direction is 0, and how can I stop it?

The current method for collision resolution is as follows:

                // Update the particle                debug_Particle.Integrate(dt_Seconds);                // Set the polygon centre to the particle position                debug_Polygon2D[1].Centre = debug_Particle.Position;                                // Test for 2D Swept SAT                 if (Detection2D.TestMovingPolygonPolygon(debug_Polygon2D[0], debug_Polygon2D[1], ref contact))                {                    T0 = contact.t0;                    T1 = contact.t1;                    // Adjust position                    debug_Polygon2D[1].Centre -= contact.normal * contact.penetration;                    // IF I ADJUST PARTICLE POSITION - No more sticking                    debug_Particle.Position -= contact.normal * contact.penetration;                    // Adjust velocity                    float vDotN = Vector2.Dot(debug_Particle.Velocity, contact.normal);                    debug_Particle.Velocity -= contact.normal * vDotN;                }                // Set polygon's centre/velocity to associated particle's position/velocity                debug_Polygon2D[1].velocity = debug_Particle.Velocity;


Thank you for your help.

This topic is closed to new replies.

Advertisement