I have been reading plenty of tutorials about sliding collision responses yet I am not able to implement it properly in my project. What I want to do is make a puck slide along the rounded corner boards of a hockey rink. In my latest attempt the puck does slide along the boards but there are some strange velocity behaviors. First of all the puck slows down a lot pretty much right away and then it slides for awhile and stops before exiting the corner. Even if I double the speed I get a similar behavior and the puck does not make it out of the corner. I used some ideas from this document http://www.peroxide.dk/papers/collision/collision.pdf.
This is what I have:
Update method called from the game loop when it is time to update the puck (I removed some irrelevant parts). I use two states (current, previous) which are used to interpolate the position during rendering.
public override void Update(double fixedTimeStep)
{
/* Acceleration is set to 0 for now. */
Acceleration.Zero();
PreviousState = CurrentState;
_collisionRecursionDepth = 0;
CurrentState.Position = SlidingCollision(CurrentState.Position, CurrentState.Velocity * fixedTimeStep + 0.5 * Acceleration * fixedTimeStep * fixedTimeStep);
/* Should not this be affected by a sliding collision? and not only the position. */
CurrentState.Velocity = CurrentState.Velocity + Acceleration * fixedTimeStep;
Heading = Vector2.NormalizeRet(CurrentState.Velocity);
}
private Vector2 SlidingCollision(Vector2 position, Vector2 velocity)
{
if(_collisionRecursionDepth > 5)
return position;
bool collisionFound = false;
Vector2 futurePosition = position + velocity;
Vector2 intersectionPoint = new Vector2();
Vector2 intersectionPointNormal = new Vector2();
/* I did not include the collision detection code, if a collision is detected the intersection point and normal in that point is returned. */
if(!collisionFound)
return futurePosition; /* If no collision was detected it is safe to move to the future position. */
/* It is not exactly the intersection point, but slightly before. */
Vector2 newPosition = intersectionPoint;
/* oldVelocity is set to the distance from the newPosition(intersection point) to the position it had moved to had it not collided. */
Vector2 oldVelocity = futurePosition - newPosition;
/* Project the distance left to move along the intersection normal. */
Vector2 newVelocity = oldVelocity - intersectionPointNormal * oldVelocity.DotProduct(intersectionPointNormal);
if(newVelocity.LengthSq() < 0.001)
return newPosition; /* If almost no speed, no need to continue. */
_collisionRecursionDepth++;
return SlidingCollision(newPosition, newVelocity);
}
What am I doing wrong with the velocity? I have been staring at this for very long so I have gone blind. I have tried different values of recursion depth but it does not seem to make it better. Let me know if you need more information. I appreciate any help.
EDIT: A combination of Patrick Hughes' and teodron's answers solved the velocity problem (I think), thanks a lot! This is the new code:
I decided to use a separate recursion method now too since I don't want to recalculate the acceleration in each recursion.
public override void Update(double fixedTimeStep)
{
Acceleration.Zero();// = CalculateAcceleration(fixedTimeStep);
PreviousState = new MovingEntityState(CurrentState.Position, CurrentState.Velocity);
CurrentState = SlidingCollision(CurrentState, fixedTimeStep);
Heading = Vector2.NormalizeRet(CurrentState.Velocity);
}
private MovingEntityState SlidingCollision(MovingEntityState state, double timeStep)
{
bool collisionFound = false;
/* Calculate the next position given no detected collision. */
Vector2 futurePosition = state.Position + state.Velocity * timeStep;
Vector2 intersectionPoint = new Vector2();
Vector2 intersectionPointNormal = new Vector2();
/* I did not include the collision detection code, if a collision is detected the intersection point and normal in that point is returned. */
/* If no collision was detected it is safe to move to the future position. */
if (!collisionFound)
return new MovingEntityState(futurePosition, state.Velocity);
/* Set new position to the intersection point (slightly before). */
Vector2 newPosition = intersectionPoint;
/* Project the new velocity along the intersection normal. */
Vector2 newVelocity = state.Velocity - 1.90 * intersectionPointNormal * state.Velocity.DotProduct(intersectionPointNormal);
/* Calculate the time of collision. */
double timeOfCollision = Math.Sqrt((newPosition - state.Position).LengthSq() / (futurePosition - state.Position).LengthSq());
/* Calculate new time step, remaining time of full step after the collision * current time step. */
double newTimeStep = timeStep * (1 - timeOfCollision);
return SlidingCollision(new MovingEntityState(newPosition, newVelocity), newTimeStep);
}
Even though the code above seems to slide the puck correctly please have a look at it.
I have a few questions, if I don't multiply by 1.90 in the newVelocity calculation it doesn't work (I get a stack overflow when the puck enters the corner because the timeStep decreases very slowly - a collision is found early in every recursion), why is that? what does 1.90 really do and why 1.90?
Also I have a new problem, the puck does not move parallell to the short side after exiting the curve; to be more exact it moves outside the rink (I am not checking for any collisions with the short side at the moment). When I perform the collision detection I first check that the puck is in the correct quadrant. For example bottom-right corner is quadrant four i.e. circleCenter.X < puck.X && circleCenter.Y puck.Y is this a problem? or should the short side of the rink be the one to make the puck go parallell to it and not the last collision in the corner?
EDIT2:
This is the code I use for collision detection, maybe it has something to do with the fact that I can't make the puck slide (-1.0) but only reflect (-2.0):
/* Point is the current position (not the predicted one) and quadrant is 4 for the bottom-right corner for example. */
if (GeometryHelper.PointInCircleQuadrant(circleCenter, circleRadius, state.Position, quadrant))
{
/* The line is: from = state.Position, to = futurePosition. So a collision is detected when from is inside the circle and to is outside. */
if (GeometryHelper.LineCircleIntersection2d(state.Position, futurePosition, circleCenter, circleRadius, intersectionPoint, quadrant))
{
collisionFound = true;
/* Set the intersection point to slightly before the real intersection point (I read somewhere this was good to do because of floting point precision, not sure exactly how much though). */
intersectionPoint = intersectionPoint - Vector2.NormalizeRet(state.Velocity) * 0.001;
/* Normal at the intersection point. */
intersectionPointNormal = Vector2.NormalizeRet(circleCenter - intersectionPoint)
}
}
When I set the intersection point, if I for example use 0.1 instead of 0.001 the puck travels further before it gets stuck, but for all values I have tried (including 0 - the real intersection point) it gets stuck somewhere (but I necessarily not get a stack overflow). Can something in this part be the cause of my problem?
I can see why I get the stack overflow when using -1.0 when calculating the new velocity vector; but not how to solve it. I traced the time steps used in the recursion (initial time step is always 1/60 ~ 0.01666):
Recursion depth Time step next recursive call
[Start recursion, time step ~ 0.016666]
0 0,000985806527246773
[No collision, stop recursion]
[Start recursion, time step ~ 0.016666]
0 0,0149596704364629
1 0,0144883449376379
2 0,0143155612984837
3 0,014224925727213
4 0,0141673917461608
5 0,0141265435314026
6 0,0140953966184117
7 0,0140704653746625
...and so on.
As you can see the collision is detected early in every recursive call which means the next time step decreases very slowly thus the recursion depth gets very big - stack overflow.