24
\$\begingroup\$

I'm making a 4 player co-op r-type game, and I'm about to implement the collision detection code. I've read a lot of articles and stuff about how to handle collision detection, but I'm having a hard time figuring out what to go with. It seems the quad tree is the most common way to go, but in some resources they mention the grid based solution. For having used a grid for detections in a previous game, I'm comfortable with that, but is it actually better than a quad tree ? I'm not sure which offers best performance, and I've also ran a little benchmark, there is not much difference between both solutions.

Is one better than the other ? or more elegant ? I'm really not sure which one I should use.

Any advice is welcome. Thanks.

\$\endgroup\$
28
\$\begingroup\$

The right answer depends a little bit on the actual game you're designing, and choosing one over the other is really going to require implementing both and doing profiling to find out which one is more time or space efficient on your specific game.

Grid detection seems to only apply to detecting collisions between moving objects and a static background. The biggest advantage to this is that the static background is represented as a contiguous memory array, and each collision lookup is O(1) with good locality if you need to do multiple reads (because entities cover more than one cell in the grid). The disadvantage, if the static background is large, is that the grid can be rather wasteful of space.

If instead you represent the static background as quadtree, then the cost of individual lookups goes up, but because large blocks of the background take up a small amount of space, the memory requirements go down, and so more of the background can sit in the cache. even if it takes 10 times as many reads to do a lookup in such a structure, if it's all in the cache, it'll still be 10 times faster than a single lookup with a cache miss.

If I were faced with the choice? I'd go with the grid implementation, because it's stupid simple to do, better spend my time on other, more interesting problems. If I notice that my game is running a little slow, I'll do some profiling and see what could use some help. If it looks like the game is spending a lot of time doing collision detection, I'd try another implementation, like a quadtree (after exhausting all easy fixes first), and find out if that helped.

Edit: I haven't got a clue how grid collision detection relates to detecting collisions of multiple, mobile entities, but instead, i'll answer how a spatial index (Quadtree) improves detection performance over the iterative solution. The naive (and typically perfectly fine) solution looks sort of like this:

foreach actor in actorList:
    foreach target in actorList:
        if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

This obviously has performance around O(n^2), with n the number of actors that are currently alive in the game, including bullets and spaceships and aliens. It can also include small static obstacles.

This works fantastically well so long as the number of such items is reasonably small, but starts to look a little poor when there's more than a few hundred objects to check against. 10 objects results in just 100 collision checks, 100 results in 10,000 checks. 1000 results in one million checks.

A spatial index (like quadtrees) can efficiently enumerate the items it collects according to geometric relationships. this would change the collision algorithm to something like this:

foreach actor in actorList:
    foreach target in actorIndex.neighbors(actor.boundingbox):
       if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

The efficiency of this (assuming a uniform distribution of entities): is usually O(n^1.5 log(n)), since the index takes about log(n) comparisons to traverse, there will be about sqrt(n) neighbors to compare, and there are n actors to check. Realistically, though, the number of neighbors is always quite limited, since if a collision does occur, most of the time one of the objects is deleted, or moved away from the collision. thus you get just O(n log(n)). For 10 entities, you do (about) 10 comparisons, for 100, you do 200, for 1000 you do 3000.

A really clever index can even combine the neighbor search with the bulk iteration, and perform a callback on each intersecting entity. This will give a performance of about O(n), since the index is being scanned once rather than queried n times.

\$\endgroup\$
  • \$\begingroup\$ I'm not sure I know what you are refering to when you say "static background". What I'm dealing with is basicaly a 2D shooter, so it's collision detection with space ships and aliens, bullets and walls. \$\endgroup\$ – dotminic Dec 10 '10 at 0:41
  • 2
    \$\begingroup\$ You just earned my private "Great answer" badge! \$\endgroup\$ – Felixyz Dec 10 '10 at 0:41
  • \$\begingroup\$ This might sound stupid but how do I actually use my quadtree to select against which other objects an object should test collisions ? I'm unsure about how this is done. Which brings up a second question. Say I have an object in node that is not a neighbor of another node, but that the object is large enough that it spans a few nodes, how can I check for an actual collision, since I'm guessing the tree might consider it's not close enough to collide with objects in a "far away" node ? Should objects that don't completely fit in a node be kept in the parent node ? \$\endgroup\$ – dotminic Dec 13 '10 at 21:47
  • 2
    \$\begingroup\$ Quat-tree's are inherintly sub-optimal for overlapping bounding box searches. The best choice for that is usually an R-Tree. For quad-trees, if most objects are roughly point-like, then yes, it's reasonable to keep objects at inner nodes, and perform exact collision testing on a fuzzy neighbor search. If most objects in the index are large and overlap without colliding, A quad tree probably is a poor choice. If you have more technical questions about this, you should consider taking them to stackoverflow.com \$\endgroup\$ – SingleNegationElimination Dec 13 '10 at 22:16
  • \$\begingroup\$ All this is pretty confusing! thanks for the info. \$\endgroup\$ – dotminic Dec 13 '10 at 22:55
1
\$\begingroup\$

Sorry for resurrecting ancient thread but IMHO plain old grids aren't used often enough for these cases. There's lot of advantages to a grid in that cell insertion/removal is dirt cheap. You don't have to bother with freeing a cell since the grid has no aim to optimize for sparse representations. I say that having reduced the time to marquee select a bunch of elements in a legacy codebase from over 1200ms down to 20ms by just replacing the quad-tree with a grid. In fairness though, that quad-tree was really poorly implemented, storing a separate dynamic array per leaf node for the elements.

The other one that I find extremely useful is that your classic rasterization algorithms for drawing shapes can be used to do searches into the grid. For example, you can use Bresenham line rasterization to search for elements that intersect a line, scanline rasterization to find what cells intersect a polygon, etc. Since I work a lot in image processing, it's really nice to be able to use the exact same optimized code I use to plot pixels to an image as I use to detect intersections against moving objects in a grid.

That said, to make a grid efficient, you shouldn't need more than 32-bits per grid cell. You should be able to store a million cells in under 4 megabytes. Each grid cell can just index the first element in the cell, and the first element in the cell can then index the next element in the cell. If you're storing some kind of full-blown container with every single cell, that gets explosive in memory use and allocations quickly. Instead you can just do:

struct Node
{
    int32_t next;
    ...
};

struct Grid
{
     vector<int32_t> cells;
     vector<Node> nodes;
};

Like so:

enter image description here

Okay, so on to the cons. I'm coming at this admittedly with a bias and preference towards grids, but their main disadvantage is that they aren't sparse.

Accessing a specific grid cell given a coordinate is constant-time and doesn't require descending down a tree which is cheaper, but the grid is dense, not sparse, so you could end up having to check more cells than required. In situations where your data is very sparsely distributed, the grid could require checking way more to figure out the elements that intersect say a line or a filled polygon or a rectangle or a bounding circle. The grid has to store that 32-bit cell even if it's completely vacant, and when you're doing a shape intersection query, you have to check those empty cells if they intersect your shape.

The quad-tree's main benefit is naturally its ability to store sparse data and only subdivide as much as needed. That said, it's harder to implement really well, especially if you have things moving around every frame. The tree needs to subdivide and free child nodes on the fly very efficiently, otherwise it degrades into a dense grid wasting overhead to store parent->child links. It's very doable to implement an efficient quad-tree using very similar techniques to what I described above for the grid, but generally going to be more time-intensive. And if you do it the way I do in the grid, that's not necessarily optimal either, since it would lead to a loss in the ability to guarantee that all 4 children of a quad-tree node are stored contiguously.

Also both a quad-tree and grid don't do a magnificent job if you have a number of large elements that span much of the entire scene, but at least the grid stays flat and doesn't subdivide to the nth degree in those cases. The quad-tree should store elements in branches and not just leaves to reasonably handle such cases or else it will want to subdivide like crazy and degrade in quality extremely rapidly. There are more pathological cases like this you have to take care of with a quad-tree if you want it to handle the widest range of content. For example, another case that can really trip up a quad-tree is if you have a boatload of coincident elements. At that point some people just resort to setting a depth limit for their quad-tree to prevent it from subdividing infinitely. The grid has an appeal that it does a decent job, kind of a jack-of-all-trades, against a wide range of inputs, and it takes a very steady and predictable amount of memory no matter what you do (32-bit overhead per cell, 32-bit overhead per element inserted in cell).

The stability and predictability is also beneficial in a game context, since sometimes you don't necessarily want the fastest solution possible for the common case if it could occasionally lead to hiccups in frame rates in rare case scenarios versus a solution that's reasonably fast all-around but never leads to such hiccups and keeps frame rates smooth and predictable. A grid has that kind of latter quality going for it.

With all that said, I really think it's up to the programmer. With things like grid vs. quad-tree or octree vs. kd-tree vs. BVH, my vote is on the most prolific developer with a record for creating very efficient solutions no matter what data structure he/she uses. There's a whole lot at the micro-level too, like multithreading, SIMD, cache-friendly memory layouts and access patterns. Some people might consider those micro but they don't necessarily have a micro impact. Such things could make a 100x difference from one solution to the other. In spite of this, if I was personally given a few days and was told I need to implement a data structure to rapidly accelerate collision detection of elements moving around every frame, I'd do better in that short time implementing a grid than a quad-tree. My probability of making a mistake would be significantly lower, my probability of ending up with a more efficient well-rounded solution in that time would be higher, I would have more time to seek micro-optimizations like SIMD, multithreading, etc, I'd be able to write a more thorough unit test potentially, create more documentation, and so forth.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.