Closed parasyte closed 10 years ago
Another great article (but I guess you know it already) that also cover this topic and collision resolution itself : http://www.metanetsoftware.com/technique/tutorialA.html
+1 for creating a AABB box representing the object motion (that for sure will fix issue #16), however this can only be used with fixed element (like Tile), right ? else an object going where the object was previously placed could trigger a false-positive collision.
Else for the phases approach, I've been looking at that as well, but considering that basically melonJS only supports AABB boxes, I didn't find how to efficiently do the broad phase, as basically (if' I'm not wrong, which I'm sure I am) it's about detecting which objects are intersecting with the object range of motions (which is already the narrow phase).
Yeah, it's a super great article that I read through a while back. (It made me decide to use chipmunk rather than roll my own collision detection, as I was short on time.)
The false positives are ok in the broad phase, as long as they can be "bailed early" and don't cause too many false positives each frame. That would be a worst-case scenario, and would probably be an uncommon situation. I think only having MANY very small & very faster moving objects will actually create a lot of false positives within the range-of-motion AABBs.
The "broad phase" is usually more about figuring out which objects should be considered for collision checking. It's generally a good idea to use spacial hashing as your broad phase. This will tell you which objects are potentially colliding. You then use their range-of-motion AABBs to further narrow down the search. The "narrow phase" will resolve those collisions; in a physics engine, this means computing and applying response forces.
@parasyte
I'm moving this one to the next release, unless you are yourself super hyper motivated in implementing this before :) note, the tiled repo is quite active recently, the long due 0.9.0 version should be quite close now, and so the melonJS 0.9.5 release (else I guess at some point I should stop waiting for it and release is without the new image layer)
@obiot ok! My motivation has turned to some other projects recently. :)
@parasyte sure, anything exciting ? I hope anyway to see you soon back on melonJS :):):)
Have fun, Olivier
Another great article : http://buildnewgames.com/broad-phase-collision-detection/
I started on this tonight, and it got me thinking of the future of melonJS. We want to integrate a physics engine officially. And physics engines do all of their collision detection with rigid bodies; even the environment is composed of rigid bodies. There isn't a distinction between collisions with the environment and collisions with other entities. I guess this property is unique to melonJS in its current form. :)
I would like to remove that distinction in this ticket. All collision checking should occur in one function, using the same data structures. This will ease the integration of a physics engine, and also clean up and make collision detection less work to manage. For compatibility purposes, I am not opposed to converting the collision tile layer into discrete objects within the new collision detection system. (Conversion happens after level load.)
The other thing I want to mention is that I probably won't be implementing an SAT algorithm here. Although I can if it makes more sense to do now than later. My concept is just to eliminate the performance issues, and fix bugs along the way. AABB collisions might be just fine for now. ;)
@parasyte
Wow, I can see it has been a busy weekend for melonjs :)
On my side i'm still away, so i'm sorry i did not provide any real feedback, but I'll be back tomorrow morning !
@obiot no worries!
My code for this ticket isn't ready for review yet. I want to keep melonJS simple, so I'm going to lump all collision phases into a single function. It isn't the "correct" way to do it, but we're not too concerned with physics accuracy anyway. :p
Regardless, this will be another big API-breaking change!
@parasyte
a few comments on my side :
body
as well, with the idea to make this notion more generic ?About big API-breaking change, please do whatever you think is the best (though I would suggest to keep it as close as possible from the physic API out-there, to ease later integration, or addition). And anyway, if we do want to bring melonJS to the "next level", that's something we cannot avoid.
I'm very excited about this one, this is one of melonjs' major remaining weakness, and if we could have some breakthrough on this one, it would really be fantastic ! :)
@obiot That all sounds totally reasonable and awesome.
For AABB collisions against the special tiles like slopes: Those will still be handled the same as current, with "pixel perfect" collisions against the 45° slopes. It will need to change later for SAT, but at least we won't drop a feature during the upgrade.
Collision layer conversion code is done! I need to take care of a few more things in the outlying areas and do some testing. After that, I'll push the initial commit to a new branch. :D
One more thing we might enforce is doing all collision handling in callbacks, and getting rid of the explicit me.ObjectEntity.collide()
and collideType()
methods. Chipmunk, for example, only supports custom collision handling in callbacks, but it supports three different callbacks for handling collisions at different phases of collision resolution.
For the me.ObjectEntity.collisionBox
, we shouldn't rename it body
, but something else like shape
or collisionShape
. A "body" in Chipmunk terms is the data structure that contains information about the position, rotation, etc. It's analogous to our me.ObjectEntity
already. :) The "shape" defines how the body may collide to with other shapes in the space. So that shape is a lot like collisionBox
. Although, Chipmunk also supports multiple shapes per body, and maybe this is something we could consider, too.
@parasyte Same, totally reasonable and awesome ;)
wow, that's become very concrete very fast I can't wait to play with the new branch !
Not sure about getting rid of updateMovement
! I was thinking about using it to call the new collision checks.
Excluding from collision is easy: I have a new property called collisionMask
which can be set in Tiled (or JavaScript) which will mask collisions as desired. If not explicitly set, collisionMask
defaults to 0xFFFFFFFF
(i.e. collide with everything). It can be used kind of like this:
var masks = {
PLAYER : 0x00000001,
ENEMY : 0x00000002,
COIN : 0x00000004,
POWERUP : 0x00000008,
};
game.PlayerObject.collisionMask = masks.PLAYER | masks.ENEMY | masks.COIN | masks.POWERUP;
game.CoinObject.collisionMask = masks.COIN;
game.EnemyObject.collisionMask = masks.ENEMY;
game.PowerupObject.collisionMask = masks.POWERUP;
Now game.PlayerObject
can collide with everything, and enemies cannot collide with coins or powerups.
It can be a bit tricky to use masks, but it's also supported in Chipmunk!
wow, that looks great, masks are actually very very good :)
about the collision check, I thought this would be actually automatically done, without requiring to manually call the function, it is not ? but I guess it will be clearer for me once I will see it :)
Well, it's no more automatic than adding gravity to the velocity vector. ;) I'm sure we could clean this all up in some way, but I haven't really thought about it.
For performance reasons, we only want to check for collisions when an entity moves. And updateMovement
is what does that right now.
oh ok, got your point :)
@obiot Here is the initial work! :dancers: It's really early still, but it's pretty cool to watch with debug enabled.
@parasyte so cool, I think I definitely need some time to read your code and understand how it works, but so far I liked what I saw, except for the try/catch :)
@parasyte just played with it a little, and effectively seeing it running in debug mode is quite cool :)
I did provide a few comments, I hope you don't mind (but nothing really constructive), as I'm still in the phase where I need to understand all the code changes :)
but hey, quite a good good good work you put here !!!!!
I have some ideas to optimize the code, but I think even the algorithm as it is will be quite a bit better for performance. My next step before working on the default collision handler will be building a basic collision test example, to better understand how it changes performance.
@obiot Funny news! The collision test demo has worse performance in this branch than in master! :p Updating the grid cells in the broad phase part takes waaayyy too much time. This is actually very easy to fix. But for now ... sleep time! I'll try again tomorrow!
@parasyte that's very good news then, because I was also playing with the code, and a bit worried about performances :)
From the test cases mentioned in #192, I'm going to stop using Array.prototype.remove()
for the me.collision.remove()
API. And there are a few other changes I want to try:
me.collision.add()
and me.collision.remove()
should set and remove the entire list of grid cells, so callers do not have to perform the iteration.me.collision.updateMovement()
so that it doesn't have to call add()
and remove()
on every frame.My idea for hashing is creating a unique string that stores the effective "rectangle" of collision grid cells that the object already belongs to, and saving it to the object. At the start of me.game.updateMovement()
, a new hash will be generated from the object's current range of motion AABB, and compare it to the saved hash. This should be really fast for determining whether the grid cells need to be updated.
This test shows that in all cases, comparing strings is faster than comparing object properties (like when using me.Rect.equals()
): http://jsperf.com/string-vs-property-comparisons So the string-hashing should be the best way to do this.
@obiot WOW!! I just added the collision_test example to master
illustrating how the changes for this ticket improve performance.
On my Mac, the collision_test runs at ~11 fps on master
. And runs at ~50 fps on ticket-103
. I like it so far. :)
@parasyte
Sorry I was out for lunch !
unfortunately, the demo on the #103 branch seems to be stucked (the smileys are not going down), so i cannot really test it ( i guess it's linked to you two last changes since your message)
but anyway, if you have been able to provide that level of optimization, it's f\ kind of f*** impressive :):):)
my god version 0.9.7 will definitely kick ass !
Yeah, I don't like the "not moving" part either! :frowning:
But try this patch, and BE AMAZED! :smile:
diff --git a/src/entity/entity.js b/src/entity/entity.js
index e105ec4..fe6d177 100644
--- a/src/entity/entity.js
+++ b/src/entity/entity.js
@@ -884,7 +884,8 @@
}
// FIXME: Handle collision with tiles
- this.vel.setZero();
+ this.vel.x += (0.5 - Math.random()) * 10;
+ this.vel.y += (0.5 - Math.random()) * 10;
},
/** @private */
wow, i'm truly amazed actually !!!!!
and 2x times amazed, as I'm not sure I would be able to put all this together by myself (at least not as fast as you)!
Now I have a solid 60 fps in collision_test on my Mac! The smaller entities make it a little bit better on master
, too. It gets 15 fps now. :laughing: Bumping the entity count to 400, I get ~38 fps on ticket-103
, and ~2 fps on master
!
Also, more importantly I started on collision reactions tonight. I have it at least partially working right now. Some bugs left: entities get frozen in air if they are touching, and high-velocity smashing causes strange things like entities bouncing in the wrong direction.
But it's a great improvement already, and a 100% solution for #16! i.e. Very-high-velocity collisions are detected appropriately.
The velocity trick is a simple but smart optimization :)
Next target is 60fps for 400 entities ! Once your code is stable I will give it a pass too for optimization, right now I'm afraid to break stuff ....
Amazing as well to see the difference with the old collision system, I knew it was not that good, but at that point no !
I was playing around with the collision example, and despite of the few bugs you mentioned, the difference is truly visually impressive :)
Solving the rest of this ticket will be the tricky part, I think. The penetration depth code is kind of working, but it's not very accurate for determining the actual depth, just an approximation. There are some edge cases it needs to handle, and I need to also integrate the linear velocity vector to make sense of the data.
I tried a few different things so far, but nothing is really working any better. Scribbled some graphs on paper to try working out the best algorithm. This is something that should be really well-documented, but I haven't been able to find any good resources on it. Most assume you're looking for an accurate collision response for a physics engine.
I'll try a few more things before attempting to look for other ideas, again. The main trouble part is here: https://github.com/melonjs/melonJS/blob/42703ad830ed99d14c0f132a1f59dfa0d7032764/src/entity/entity.js#L886 It's a very simple algorithm that attempts to correct the velocity vector so the moving entity does not intersect with the second object. As simple as it is, it's not right because it does really strange things. I might need to actually use some if-statements instead of relying on pure dynamic maths!
@obiot If you have ideas, I'd love to hear them. :)
did you actually read the following article : http://www.wildbunny.co.uk/blog/2011/12/14/how-to-make-a-2d-platform-game-part-2-collision-detection/
it's quite interesting and very well documented, although they keep managing tile as tile, as opposed to regular rectangular object on our side, so i guess the solution they propose for the internal edges is not really applicable :)
I will try on my side to play with it, and see if I'm able to propose a solution !
@parasyte just to be clear about my understanding the onCollision function is now used for both collision with entities and the environment (world), right ?
Then if the case at that line, current velocity should not just be cancel (= 0) ? https://github.com/melonjs/melonJS/blob/42703ad830ed99d14c0f132a1f59dfa0d7032764/src/entity/entity.js#L886
@obiot You are right, onCollision
now handles all collisions. But the state of the objects at this callback is [usually] that they are not yet touching. Just that the currently moving object (this
) is about to intersect, given its currently velocity.
I was originally just setting velocity to zero in the handler: https://github.com/melonjs/melonJS/blob/7f6996f0e62d9264001adeec4f164407f6d83f19/src/entity/entity.js#L886 This was undesirable because it makes falling entities "slow down" when they get closer to the solid ground tiles, instead of just landing. (Because the entities are not yet intersecting the ground tiles.) Zeroing the velocity also unfortunately makes it impossible to walk; gravity tries pushing the entity into the ground tiles, and then the velocity along both axes gets zeroed.
The updated code attempts to make it better by calculating the penetration depth, and adjusting the velocity to be flush with the colliding object. I think this is the right way to handle it, but there are problems with floating point precision which causes entities to slightly bounce when they are supposed to be resting on a solid tile (gravity, again). That was the purpose for the final floorSelf()
on the velocity... even though that is incorrect, too!
I'm going to read over the wildbunny article again (I know I've seen it before). Maybe it will provide some new insight!
@parasyte
there is not a trick where we actually need to store the space difference between the 2 objects and then apply when required ? this is what I actually did in my poor implementation :)
@parasyte
I mean, once we know 2 entities will collide, we should clamp the entity position with the delta space between the 2 of them ? (i don't know if i'm clear).
@obiot You are clear; that's what the code attempts to do. It's just wrong. Effectively, it clamps the velocity, which gets integrated into the position after all collision callbacks have run for this
entity.
It's really to bad we cannot sit together and have a pass together on the code, it would be that much easier :(
Another good update tonight! This one fixes a lot of the bugs in collision response, making the platformer example almost fully playable, and the smilies in collision test now stack nicely! It's no physics engine, but it will be good enough for simple games.
I have changed a few things with the API (again). Most importantly, the onCollision
callback shouldn't be responsible for applying the collision response, because the callback needs the penetration depth information to accurately tell the directionality of collisions. The default collision response can be disabled when the callback returns false. This is in line with Chipmunk-js, where the pre-solve callback can be used to disable the response.
The code for calculating the collision response is getting ugly now, but I don't think it can be prevented! :(
wow great ! on my side I however did not have the time to do anything today, as I spent my day in meetings :( will you commit it tonight ?
@obiot The commit was pushed last night just before my comment. :) Still not perfect, but much closer now!
@parasyte weirdly it does not show up when I display the branch list (i guess some cache issue with my browser!)
Else, this morning I had some chat with Noel (tokyonono), he was asking about our (well your) implementation as he spent lots of time himself implementing the wildbunny one, and he had some questions and comments he should soon provide to use here (that's good !)
Nice! I'd love to get some additional feedback. It sounds like some API changes might be needed, looking at your code review comments. But every little bit helps!
Adding this as a reminder for later:
TODO: Need to add "collision group" support, like Chipmunk. A collision group is a property on an object (similar to but distinct from the new collisionMask
property) that allows filtering objects before AABB tests if the groups match. This is useful for example with bullets; usually you don't want bullets to collide with each other. (It would also be helpful for static collision shapes, but they don't generate collisions with the current implementation anyway; collisions are only generated when an object moves.)
Using it is very simple. By default, all objects will have collisionGroup = 0
. And any non-zero collisionGroup
will require objA.collisionGroup !== objB.collisionGroup
before checking AABB overlaps. In other words, collisionGroup = "bullet"
will never collide with any other object in the "bullet" collision group.
Chipmunk documentation for collision groups: http://chipmunk-physics.net/release/ChipmunkLatest-Docs/#cpShape-Filtering
Just for info, my implementation of the wild bunny approach in Cocos2d-html5:
tileCoordForPosition:function(position){
// helper method for physics
var x = Math.floor(position.x / this.map.getTileSize().width);
var levelHeightInPixels = this.map.getMapSize().height * this.map.getTileSize().height;
var y = Math.floor((levelHeightInPixels - position.y) / this.map.getTileSize().height);
return cc.p(x, y);
},
tileRectFromTileCoords:function(tileCoords){
// helper method for physics
var levelHeightInPixels = this.map.getMapSize().height * this.map.getTileSize().height;
var origin = cc.p(tileCoords.x * this.map.getTileSize().width, levelHeightInPixels - ((tileCoords.y + 1) * this.map.getTileSize().height));
return cc.rect(origin.x, origin.y, this.map.getTileSize().width, this.map.getTileSize().height);
},
getRelevantTilesForCollisionCheck:function(character){
// get collision box
var collisionBox = character.collisionBoundingBox();
// get desired position box origin coordinates
var desiredOriginX = character.desiredPosition.x-(collisionBox.width/2);
var desiredOriginY = character.desiredPosition.y-(collisionBox.height/2);
// get surrounding box containing both the collision box and the desired position box
var pointBottomLeft = cc.p(Math.min(collisionBox.x,desiredOriginX),Math.min(collisionBox.y,desiredOriginY));
var pointTopRight = cc.p(Math.max(collisionBox.x,desiredOriginX)+collisionBox.width,Math.max(collisionBox.y,desiredOriginY)+collisionBox.height);
var pointBottomRight = cc.p(pointTopRight.x,pointBottomLeft.y);
var pointTopLeft = cc.p(pointBottomLeft.x,pointTopRight.y);
// get coordinates of tiles at each extremity
var bottomLeft = this.tileCoordForPosition(pointBottomLeft);
var topLeft = this.tileCoordForPosition(pointTopLeft);
var bottomRight = this.tileCoordForPosition(pointBottomRight);
var topRight = this.tileCoordForPosition(pointTopRight);
// get max values for map
var mapMaxX = this.map.getMapSize().width;
var mapMaxY = this.map.getMapSize().height;
// get surrounding tiles
var surroundingTiles = new Array();
for(var positionY = topLeft.y; positionY <= bottomLeft.y; positionY++){
if ( positionY >= 0 && positionY < mapMaxY){
for(var positionX = bottomLeft.x; positionX <= bottomRight.x; positionX++){
if ( positionX >= 0 && positionX < mapMaxX){
var tilePos = cc.p(positionX, positionY);
surroundingTiles.push(tilePos);
}
}
}
}
return surroundingTiles;
},
distanceBetweenRectangles:function(rect1,rect2){
// Minkowski difference
var center1 = cc.p(cc.rectGetMidX(rect1),cc.rectGetMidY(rect1));
var center2 = cc.p(cc.rectGetMidX(rect2),cc.rectGetMidY(rect2));
var diffX = center2.x - center1.x;
var diffY = center2.y - center1.y;
var halfwidth = (rect1.width + rect2.width)/2;
var halfheight = (rect1.height + rect2.height)/2;
// get normal vector
var normalVector = null;
var distanceX = Math.abs(diffX) - halfwidth;
var distanceY = Math.abs(diffY) - halfheight;
if(Math.abs(distanceX) < Math.abs(distanceY)) {
if(distanceX < 0) normalVector = cc.p(0,0);
else if(diffX<0) normalVector = cc.p(-distanceX,0);
else normalVector = cc.p(distanceX,0);
} else {
if(distanceY < 0) normalVector = cc.p(0,0);
else if(diffY<0) normalVector = cc.p(0,-distanceY);
else normalVector = cc.p(0,distanceY);
}
return normalVector;
},
isInternalCollision:function(tile,direction){
// get tile next to the current one in direction of the normal
var nextToTile = null;
if(direction.y<0) nextToTile = cc.p(tile.x,tile.y-1);
else if(direction.y>0) nextToTile = cc.p(tile.x,tile.y+1);
else if(direction.x<0) nextToTile = cc.p(tile.x+1,tile.y);
else if(direction.x>0) nextToTile = cc.p(tile.x-1,tile.y);
else return false;
// get max values for map
var mapMaxX = this.map.getMapSize().width;
var mapMaxY = this.map.getMapSize().height;
// check if the tile next to the current one is collidable
if(nextToTile.x >= 0 && nextToTile.x < mapMaxX && nextToTile.y >= 0 && nextToTile.y < mapMaxY){
var tgid = this.collisionLayer.getTileGIDAt(nextToTile);
if (tgid) return true;
}
return false;
},
checkForCollisions:function(character){
// set onGround property
character.onGround = false;
// buffer for the edge
var buffer = 0.01;
// get relevant tiles for collision check
var relevantTiles = this.getRelevantTilesForCollisionCheck(character);
// get current velocity and collision box
var stepVelocity = cc.pSub(character.desiredPosition, character.getPosition());
var collisionBox = character.collisionBoundingBox();
// go threw all relevant tiles
for (var tileIndex = 0; tileIndex < relevantTiles.length; tileIndex++) {
// check for collision only if it is collidable
var tgid = this.collisionLayer.getTileGIDAt(relevantTiles[tileIndex]);
if(tgid){
// check for collision (speculative contacts)
var tileRect = this.tileRectFromTileCoords(relevantTiles[tileIndex]);
var normalDistance = this.distanceBetweenRectangles(collisionBox,tileRect);
// check on X axis
if(normalDistance.x && Math.abs(normalDistance.x) < Math.abs(stepVelocity.x)){
if(!this.isInternalCollision(relevantTiles[tileIndex],normalDistance)){
if(normalDistance.x>0) stepVelocity.x = normalDistance.x - buffer;
if(normalDistance.x<0) stepVelocity.x = normalDistance.x + buffer;
character.velocity = cc.p(0.0, character.velocity.y);
}
}
// check on Y axis
else if(normalDistance.y && Math.abs(normalDistance.y) < Math.abs(stepVelocity.y)){
if(!this.isInternalCollision(relevantTiles[tileIndex],normalDistance)){
if(normalDistance.y>0) stepVelocity.y = normalDistance.y - buffer;
else if(normalDistance.y<0){
stepVelocity.y = normalDistance.y + buffer;
character.onGround = true;
if (character.isJumping) character.endJump();
}
character.velocity = cc.p(character.velocity.x, 0.0);
}
}
else if(normalDistance.x == 0 && normalDistance.y == 0){
// fix for diagonals (Minkowski difference)
var center1 = cc.p(cc.rectGetMidX(collisionBox),cc.rectGetMidY(collisionBox));
var center2 = cc.p(cc.rectGetMidX(tileRect),cc.rectGetMidY(tileRect));
var diffX = center2.x - center1.x;
var diffY = center2.y - center1.y;
var halfwidth = (collisionBox.width + tileRect.width)/2;
var halfheight = (collisionBox.height + tileRect.height)/2;
var distanceX = Math.abs(diffX) - halfwidth;
var distanceY = Math.abs(diffY) - halfheight;
if(Math.abs(distanceX) > Math.abs(distanceY)) {
if(diffX<0) stepVelocity.x = distanceX - buffer;
else stepVelocity.x = -distanceX + buffer;
character.velocity = cc.p(0.0, character.velocity.y);
} else {
if(diffY<0) stepVelocity.y = distanceY - buffer;
else{
stepVelocity.y = -distanceY + buffer;
character.onGround = true;
if (character.isJumping) character.endJump();
}
character.velocity = cc.p(character.velocity.x, 0.0);
}
}
}
}
var newPosition = cc.pAdd(character.getPosition(), stepVelocity);
character.setPosition(newPosition);
}
:)
Still, I switched to Chipmunk even for basic stuffs because: 1/ It is better than whatever I tried by myself, and I tried a lot of things (but always legal :-) ) 2/ You can argue that the perf are not as good as Box2D when you do crazy things, but for collision detection, it does the job 3/ I stopped trying re-inventing the wheel 4/ I always found limits in all the different approaches, and it is hard to maintain later on... Chipmunk is maintained and getting better and better all the time 5/ I needed to add a moving platform, and it was too time consuming to figure out what to change in my physics engine to make it work... In my opinion, now you are developing a simple physics engine which will end up... having similar perf than Chipmunk doing simple collision detection. I am not old enough to be wise, do whatever you want guys :-D
The only reason Box2D performance is good on CocoonJS is because its compiled into the app (Objective-C). We don't have a chance to compete with the performance of native code; any JS physics engine will be slower. Until Chipmunk is also available as a native API, we may as well piggyback off the native Box2D. ;)
Very interesting speed optimization for you guys!
Any time an object's absolute velocity is < 1 on both axes, I skip the collision detection entirely. The performance increase when doing this is actually quite substantial! About 25% faster in the general case (with gravity = 0.98)
As a result, it means velocities less than 1 are not usable. Gravity works because it's an additive velocity. But I can't do things like moving an object by half a pixel per frame with this.vel.x = 0.5;
:(
I'm trying to get the best of both worlds (speedy + sub-pixel velocities) but it's proven tricky so far!
yeah, I realized the same issue event with my simple collision code. Maybe a way would be to somehow separate it and have some real velocity
vs theoretical velocity
with only the latter containing sub-pixel values ? anyway final position is rounded as well to avoid sub-pixel positioning (bad for performances and create some kind of blurry artifacts).
Objects should only do collision detection when they move. And the detection should be done in multiple phases using hash segments:
1. Broad phase
The first phase is called the "broad phase", and it only attempts to resolve which objects/tiles collide, not how to handle the collision. In the broad phase, an AABB is created for the object's entire range of motion. See image below:
In resolving the broad phase, all range-of-motion AABBs will be stored into their closest hash segments, and collisions detected between other AABBs within those segments.
(Here "hash segment" means a square area of the world. A good starting point is to break the world into hash segments that are 4x4 tiles in size, though any size is possible.)
2. Narrow phase
The narrow phase takes the collisions detected in the broad phase and finally resolves them by collision shape/position. Some broad-phase collisions will be false-positives, where the range-of-motion AABBs intersect, but an actual object collision did not take place. The narrow phase needs to be good at "bailing early" in these kinds of cases.
An algorithm like this should reduce the time complexity from
O(n^2)
to something more likeO(log((n-s)^2))
, wheren
is the total number of objects ands
is the number of stationary objects. In other words, a huge performance gain in collision detection with a large number of objects.