I'm having hard time learning collision detection by experience. I'm making a box game, à la Minecraft, and am at the stage of implementing collision detection.
I've done x and y axis', since everything consists of cubes I would like to make my own collision detector and make it as light as possible.
Is there a way to make "pixel perfect" collisions, that is when the player's bounding box (or circle) touches a box it registers as a collision? Right now this is what I did:
if(-TOUCH_DISTANCE-1 < yPlayer-yBox && yPlayer-yBox < TOUCH_DISTANCE-1)
{
collisionNorth = true;
}
if(-TOUCH_DISTANCE+1 < yPlayer-yBox && yPlayer-yBox < TOUCH_DISTANCE+1)
{
collisionSouth = true;
}
It basically detects the collision within a certain margin and that means errors, which I don't like :(. Notice the +/-1 which offsets the "collision wall" to the respective side of the box.
This works, on lower speeds, but once there's some action (when I crack up the speed variable) the collision can't be detected anymore since I go too fast and pass right through the cube... Is there a way to make it wallhax0r proof?
This is especially annoying on z axis, when the player falls at high speed and even when defining a respectable collision margin it will ultimately look nasty (player half buried).
You've identified one of the problems of using discrete mathematics to model the path of an object. At time t the object is "here" and at time t + delta it's "there" - without actually having passed through the points in between.
You can get more accuracy by decreasing delta, but ultimately you are going to hit the limit of what you can calculate in that time interval.
If you can detect a collision approaching by using a relatively large time delta and loose bounding box you could then crank up the accuracy, but again you are going to hit limits.
Converting to a continuous model might help - but may take more computational power. You could switch to this when your objects are close so you're not doing it all the time.
Sorry I've not got a definite answer, only pointers.
Usually what you need to do is keep track of the previous position of the objects as well as the current position. Then when you update, you can check if the objects intersected during the time period, rather than if they intersect 'at the moment'.
Related
I have a physics engine that uses AABB testing to detect object collisions and an animation system that does not use linear interpolation. Because of this, my collisions act erratically at times, especially at high speeds. Here is a glaringly obvious problem in my system...
For the sake of demonstration, assume a frame in our animation system lasts 1 second and we are given the following scenario at frame 0.
At frame 1, the collision of the objects will not bet detected, because c1 will have traveled past c2 on the next draw.
Although I'm not using it, I have a bit of a grasp on how linear interpolation works because I have used linear extrapolation in this project in a different context. I'm wondering if linear interpolation will solve the problems I'm experiencing, or if I will need other methods as well.
There is a part of me that is confused about how linear interpolation is used in the context of animation. The idea is that we can achieve smooth animation at low frame rates. In the above scenario, we cannot simply just set c1 to be centered at x=3 in frame 1. In reality, they would have collided somewhere between frame 0 and frame 1. Does linear interpolation automatically take care of this and allow for precise AABB testing? If not, what will it solve and what other methods should I look into to achieve smooth and precise collision detection and animation?
The phenomenon you are experiencing is called tunnelling, and is a problem inherent to discrete collision detection architectures. You are correct in feeling that linear interpolation may have something to do with the solution as it can allow you to, within a margin of error (usually), predict the path of an object between frames, but this is just one piece of a much larger solution. The terminology I've seen associated with these types of solutions is "Continuous Collision Detection". The topic is large and gets quite complex, and there are books that discuss it, such as Real Time Collision Detection and other online resources.
So to answer your question: no, linear interpolation on its own won't solve your problems*. Unless you're only dealing with circles or spheres.
What to Start Thinking About
The way the solutions look and behave are dependant on your design decisions and are generally large. So just to point in the direction of the solution, the fundamental idea of continuous collision detection is to figure out: How far between the early frame and the later frame does the collision happen, and in what position and rotation are the two objects at this point. Then you must calculate the configuration the objects will be in at the later frame time in response to this. Things get very interesting addressing these problems for anything other than circles in two dimensions.
I haven't implemented this but I've seen described a solution where you march the two candidates forward between the frames, advancing their position with linear interpolation and their orientation with spherical linear interpolation and checking with discrete algorithms whether they're intersecting (Gilbert-Johnson-Keerthi Algorithm). From here you continue to apply discrete algorithms to get the smallest penetration depth (Expanding Polytope Algorithm) and pass that and the remaining time between the frames, along to a solver to get how the objects look at your later frame time. This doesn't give an analytic answer but I don't have knowledge of an analytic answer for generalized 2 or 3D cases.
If you don't want to go down this path, your best weapon in the fight against complexity is assumptions: If you can assume your high velocity objects can be represented as a point things get easier, if you can assume the orientation of the objects doesn't matter (circles, spheres) things get easier, and it keeps going and going. The topic is beyond interesting and I'm still on the path of learning it, but it has provided some of the most satisfying moments in my programming period. I hope these ideas get you on that path as well.
Edit: Since you specified you're working on a billiard game.
First we'll check whether discrete or continuous is needed
Is any amount of tunnelling acceptable in this game? Not in billiards
no.
What is the speed at which we will see tunnelling? Using a 0.0285m
radius for the ball (standard American) and a 0.01s physics step, we
get 2.85m/s as the minimum speed that collisions start giving bad
response. I'm not familiar with the speed of billiard balls but that
number feels too low.
So just checking on every frame if two of the balls are intersecting is not enough, but we don't need to go completely continuous. If we use interpolation to subdivide each frame we can increase the velocity needed to create incorrect behaviour: With 2 subdivisions we get 5.7m/s, which is still low; 3 subdivisions gives us 8.55m/s, which seems reasonable; and 4 gives us 11.4m/s which feels higher than I imagine billiard balls are moving. So how do we accomplish this?
Discrete Collisions with Frame Subdivisions using Linear Interpolation
Using subdivisions is expensive so it's worth putting time into candidate detection to use it only where needed. This is another problem with a bunch of fun solutions, and unfortunately out of scope of the question.
So you have two candidate circles which will very probably collide between the current frame and the next frame. So in pseudo code the algorithm looks like:
dt = 0.01
subdivisions = 4
circle1.next_position = circle1.position + (circle1.velocity * dt)
circle2.next_position = circle2.position + (circle2.velocity * dt)
for i from 0 to subdivisions:
temp_c1.position = interpolate(circle1.position, circle1.next_position, (i + 1) / subdivisions)
temp_c2.position = interpolate(circle2.position, circle2.next_position, (i + 1) / subdivisions)
if intersecting(temp_c1, temp_c2):
intersection confirmed
no intersection
Where the interpolate signature is interpolate(start, end, alpha)
So here you have interpolation being used to "move" the circles along the path they would take between the current and the next frame. On a confirmed intersection you can get the penetration depth and pass the delta time (dt / subdivisions), the two circles, the penetration depth and the collision points along to a resolution step that determines how they should respond to the collision.
I want to do a project, which will consist in detecting possible collision of the pool balls, using opencv, webcam and C++ programming language. For now I just want to prediction collision of 2 balls on minibilard table. I detect them by change rgb to hsv and then use thereshold, in future i will probably use another method for detect a random amount of balls, but it's not so important now.
So, for now I can detect two balls, i know their position, radius, now I'm thinking how to predict whether there will be a collision between them, if we if we assume that they will move in straight lines. I think that I should check their position in every frame update (and i have to know a time between frames in my webcamera) and by that, i I will be able to determine the value of speed, acceleration and direction of the ball. So, if i will know those parameters for for both balls, I will be able to determine where can they collide, and then, using parametric equastion I will be able to check, if they will be on collision point on the same time.
I wonder if this is the right approach to the problem, maybe there is a simpler and more effective method to do this?
Thanks for any kind of help.
Karol
This sounds like you are on track for a good project...
Calculating acceleration seems, from what I briefly read here, reasonably difficult though. So as a preliminary step, you could just assume a constant velocity. So take the difference between a balls position last frame and current frame as a vector and add it on to the current frames position to find where it will be next frame. Doing this for both balls will allow you to check for a collision.
You can check for a collision by comparing the distance between the balls centers using Pythagoras to the sum of their radii. If the sum of their radii will be greater than the distances between their centers, you have a collision.
Obviously, calculating one frame ahead is not very useful, but if you assume a constant velocity or manage to calculate their acceleration, there is no reason to why you can't calculate 30 or 100 frames in the future with this method.
I recently made a billiards ball simulation in javascript which you could take a quick look at here if you want to see how this could work.
I am trying to write a very stripped down, simple collider similar to Box2D-- absent all physics, rotation, etc. I'm doing this both to keep the code footprint tiny and understandable, and also to simply learn the inner workings of these things personally.
All I'm trying to do is collide circles and lines, and keep them from getting embedded in eachother.
Box2D does this almost perfectly-- very tiny amounts of overlap! However, when I write my own simple simulator, I get a lot of overlap: .
When I run the same simulation using Box2D (this is just all circles chasing a point in the center of the screen), I get no visible overlap at ALL.
In pseudocode, this is what I do:
For each Circle In List:
Determine who will collide with the circle in next step
Sort collisions by closest first
For each possible collision:
Add the unembed vector to the Circle's movement vector
...and then:
For each Circle In List:
At the movement to the circle
So, if the circles don't get pushed into anything else, this also works perfectly. When things pile up, though, it does NOT work, and I know why-- the unembeds simply accumulate and everyone pushes and jostles because later circles get unembedded into earlier circles, and at the end of the simulation, some are just stuck inside others. Makes enormous sense.
Here's where I'm confused:
Near as I can tell, Box2D operates EXACTLY the same way-- get possible collisions, unembed from eachother, done. But Box2D never, ever, gets overlapping like mine (or it gets them so small as to not matter).
Can someone tell me what step I have missed here? I can do tweaking to improve things (like iterating anyone who collided again and again... but Box2D does not appear to do this, and I want to understand while keeping the code light and fast).
Thanks!
Pertinent real code below:
aO->mPos = x,y of object
aO->mMove = x,y of movement this step
aO->mRadius = radius of object
aO->MovingBound() = bound of object including the move
void Step()
{
EnumList(MCObject,aO,mMovingObjectList)
{
mTree.GetAllNearbyObjects(aO->MovingBound().Expand(aO->mRadius/4),&aHitList);
aHitList-=aO; // Remove self from list
if (aHitList.GetCount()>0)
{
// Sort the collisions by closest first
if (mSortCollisions)
{
// Snip, took this out for clarity...
// It just sorts aHitList by who is closest
// to the current object
}
// Apply the unembedding
EnumList(MCObject,aO2,aHitList) CollideObjectObject(aO,aO2);
}
}
// Do the actual moves
EnumList(MCObject,aO,mMovingObjectList)
{
mTree.Move(aO->mProxy,aO->Bound(),aO->mMove);
aO->mPos+=aO->mMove;
aO->mMove=0;
}
}
void CollideObjectObject(MCObject* theO1, MCObject* theO2)
{
float aOverlap=gMath.DistanceSquared(theO1->mPos+theO1->mMove,theO2->mPos+theO2->mMove);
float aMixRadius=theO1->mRadius+theO2->mRadius;
if (aOverlap<=aMixRadius*aMixRadius)
{
Point aUnembed=(theO1->mPos-theO2->mPos);
float aUnembedLength=aMixRadius-sqrt(aOverlap);
aUnembed.SetLength(aUnembedLength);
float aMod=.5f;
if (theO2->mCollideFlags&COLLIDEFLAG_STATIONARY) aMod=1.0f;
theO1->mMove+=aUnembed*aMod;
}
}
Resolving collisions between many objects is quite a difficult problem because, in addition to the basic maths of collisions, you have to work much more diligently at fixing the accumulation of mathematical errors that come from approximative solvers (physics in the real world work based on integration which dictates infinitesimally small time-steps; while in our simulations, we usually only solve some 60 times a second).
Let's have a look at Box2D's constraint solver loop, located in b2island.cpp: In every world step, the collision resolver does not only run once. It will repeat velocityIterations times, which in the official test cases is usually set to 6 or 8. And that is what you will have to do as well.
I have a small application I have built where there are a few balls on a blank background. They all start flying through the air and use the physics I wrote to bounce accurately and have realistic collision responses. I am satisfied with how it looks except I have an issue where when my balls land directly on top of each other, the attach together and float directly up.
Here are the functions involved
https://gist.github.com/anonymous/899d6fb255a85d8f2102
Basically if the Collision function returns true, I use the ResolveCollision to change their velocities accordingly.
I believe the issue is from the slight re-positioning I do in ResolveCollision(). If they collide I bring them a frame or so backwards in location so that they are not intersecting still the next frame. However, when they are directly on top they bounce off eachother at such small bounces that eventually stepping back a frame isn't enough to unhook them.
I'm unsure if this is the problem and if it is, then what to do about it.
Any help would be awesome!
The trick is to ignore the collision if the circles are moving away from each other. This works so long as your timestep is small enough relative to their velocities (i.e. the circles can't pass through each other in a single frame).
When the circles first collide, you will adjust their velocity vectors so their relative velocity vector pushes them apart (because a collision will do that). After that, any further collisions are spurious because the circles will be moving apart, and will eventually separate completely. So, just ignore collisions between objects that are moving apart, and you should be fine.
(I've implemented such an algorithm in a 3D screensaver I wrote, but the algorithm is entirely dimension-agnostic and so would work fine for 2D circles).
I'm working on an infinite runner and I need to have collision detection between a single sprite, and any sprite out of a CCArray of sprites. How do you suggest that I do this? Currently this is the method that I call to check for collisions, but it isn't working.
bool RunningScene::spritesAreColliding(cocos2d::CCSprite *spr1, cocos2d::CCSprite *spr2)
{
//Take the bounding box of the two sprites that are bounded
CCRect r1 = spr1->boundingBox();
CCRect r2 = spr2->boundingBox();
if (r1.intersectsRect(r2)) { //Check if the bounding boxes are intersecting and return a true/false
return true;
} else {
return false;
}
}
I use it in an if statement, and if it returns true, the if statement works.
I'm not hoping for pixel perfect collision detection, but I'm wondering if that method will work for checking collisions, and I'm wondering how to access any sprite from the CCArray that happens to be colliding with the _runner sprite.
I'm not sure why your code isn't working, because I can't see what you are doing. Suppose that the method returns true. What do you do?
Collision detection is difficult. I suggest you check out the appropriate chapters of this book: Real Time Collision Detection.
There are two approaches: continuous and discrete.
It looks like you're trying to use discrete collision detection. This is when you react to collisions after they happen. You need to find the penetration axis, so that you know in which direction to 'push the boxes apart'. One simple yet slow way to accomplish this is to do a sort of 'binary search' for the moment just before they began colliding. Take the objects to their positions before their velocities were applied (start of frame). Now apply half of their velocities. Are they intersecting? If not, apply 0.75 of their velocities, and if yes, apply 0.25 of their velocities. Repeat for a preset number of iterations and until they are no longer intersecting.
You will also have to set their velocities so that they either 'bounce back' or at least stop moving along the axis that they last intersected on.
Discrete collision detection is problematic. The most well known problem is tunneling. This is when objects are moving so fast that they do not intersect. When their velocities are applied, the 'move through one another' and the collision is never caught. For 2D collision detection, I recommend the continuous approach.