2

So I'm using Box2D for collision detection in a game. I have a tilemap that contains information on the terrain: for now it's just a char[][] that has either road or grass. Now, at the start of each level I wanted to create rectangles to describe the different terrains, but I wanted these rectangles to be optimized and apparently that takes quite an algorithm.

My first approach was to create an individual terrain for EVERY tile in the map at the start of the level. The FPS was reduced to 5.

My second idea was to simply create the different rectangles for terrains as the player moved along the map, deleting the rectangles that were out of view. Although it would still be a lot of rectangles, it would be considerably less.

I haven't attempted the second method yet, but I want to know: is there any easy way for me to efficiently perform collision detection against terrain with a large tilemap?

Thanks.

helsont
  • 4,347
  • 4
  • 23
  • 28
  • How large a tilemap? I've done maybe 1000x1000 tiles. – William Morrison Jul 25 '13 at 02:07
  • By simply adding them all at once in Box2D? Wow, that's impressive. I'm using a measly 100x100. – helsont Jul 25 '13 at 02:09
  • You can use the shape class's overlap method. That's not what its called but I forget what it is called. Its in the javadoc for shape. – scott_fakename Jul 25 '13 at 02:09
  • You using java? I've got some great code I can give you to combine collision volumes. Let me post an answer... – William Morrison Jul 25 '13 at 02:10
  • I didn't post my code as its messy. Let me know if you need clarification please. – William Morrison Jul 25 '13 at 02:22
  • I'm sure you shouldn't get as little as 5 FPS if you approach the rendering correctly. A bit of research may get you to a more efficient method. Oh, and try to avoid unnecessarily dealing with off-screen stuff. Well, if having them there doesn't cause them to be rendered, and they're pretty much static, it could be faster (it's something to benchmark), although you could run into issues if you want to extend the total number of tiles beyond the amount of boxes that will fit into memory. – Bernhard Barker Jul 25 '13 at 08:02
  • `"... shouldn't get as little as 5 FPS ..."` for rendering tile by tile. – Bernhard Barker Jul 25 '13 at 11:52

1 Answers1

4

Try combining tiles. For example, if you have 16 rectangular collision volumes for 16 tiles like so...

* * * *
* * * *
* * * *
* * * *

You can obviously combine these tiles into one large rectangle.

Now, things get more difficult if you have tiles in a weird arrangement, maybe like this...

**---
****-
*--**
-*-*-

I just recently solved this problem in my game using a quad tree and sweep and prune. (Sweep and prune isn't strictly necessary, its an optimization.)

Quad tree partitions your square tiles into bigger rectangles, then you iterate over the rectangles the quad tree produces, and combine them if they have the same width, then iterate over them again and combine them by similar heights. Repeat until you can't combine them anymore, then generate your collision volumes.

Here's a link to a question I asked about a more optimal reduction. I probably won't implement this as it sounds difficult, and my current approach is working well.

Some code:

do {
    lastCompressSize = currentOutput;
    this.runHorizontalCompression(this.output1, this.output2);
    this.output1.clear();
    this.runVerticalCompression(this.output2, this.output1);
    this.output2.clear();
    currentOutput = this.output1.size;
    iterations += 1;
}while (lastCompressSize > currentOutput);

public void runHorizontalCompression(Array<SimpleRect> input,
        Array<SimpleRect> output) {
    input.sort(this.xAxisSort);
    int x2 = -1;
    final SimpleRect newRect = this.rectCache.retreive();
    for (int i = 0; i < input.size; i++) {
        SimpleRect r1 = input.get(i);
        newRect.set(r1);
        x2 = newRect.x + newRect.width;
        for (int j = i + 1; j < input.size; j++) {
            SimpleRect r2 = input.get(j);
            if (x2 == r2.x && r2.y == newRect.y
                    && r2.height == newRect.height) {
                newRect.width += r2.width;
                x2 = newRect.x + newRect.width;
                input.removeIndex(j);
                j -= 1;
            } else if (x2 < r2.x)
                break;
        }
        SimpleRect temp = this.rectCache.retreive().set(newRect);
        output.add(temp);
    }
}

public void runVerticalCompression(Array<SimpleRect> input,
        Array<SimpleRect> output) {
    input.sort(this.yAxisSort);
    int y2 = -1;
    final SimpleRect newRect = this.rectCache.retreive();
    for (int i = 0; i < input.size; i++) {
        SimpleRect r1 = input.get(i);
        newRect.set(r1);
        y2 = newRect.y + newRect.height;
        for (int j = i + 1; j < input.size; j++) {
            SimpleRect r2 = input.get(j);
            if (y2 == r2.y && r2.x == newRect.x
                    && r2.width == newRect.width) {
                newRect.height += r2.height;
                y2 = newRect.y + newRect.height;
                input.removeIndex(j);
                j -= 1;
            } else if (y2 < r2.y)
                break;
        }
        SimpleRect temp = this.rectCache.retreive().set(newRect);
        output.add(temp);
    }
}
Community
  • 1
  • 1
William Morrison
  • 10,953
  • 2
  • 31
  • 48
  • So I did a little research on quad trees just before this (I also want to implement a zoom in/zoom out feature) and I'm a little confused as to how I would use sweep and prune to create the rectangles. Sweep and prune seems to work for collision detection, and I don't want these rectangles to overlap. They will be adjacent, as you posted in your question. What am I missing here? – helsont Jul 25 '13 at 02:31
  • After running quad tree, you'll have a bunch of rectangles. None of these rectangles will overlap, but some will be adjacent. Sweep and Prune sorts rectangles by X-axis, then by Y-axis to quickly determine if rectangles are in collision. You can do the same to quickly determine if rectangles are adjacent (if they share an edge.) That's all I meant. Sort your QuadTree output by X and y values so you only compare adjacent rectangles more quickly. – William Morrison Jul 25 '13 at 02:49
  • I've posted my Sweep and Prune code. I've got some stuff in there that's odd, for instance I'm caching rectangles to keep from generating garbage (unnecessary as this will only be done once, not repeatedly.) Now I've done about 50% the work for you :). Just implement quad tree and you're home free! @user1264811 – William Morrison Jul 25 '13 at 04:01