Sign in to follow this  
Spa8nky

Collision response with velocity not correct with rectangle corners?

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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[i].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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this