0
\$\begingroup\$

Problem:

I have 32x32 world tiles, and a 64x64 object. I am only drawing visible tiles around the player. The object has its x and y coordinates in the tile world. I am drawing the object after I've drawn the visible area.

I'm drawing the object like this :

for (int y = firstY; y < lastY; y++) {
        for (int x = firstX; x < lastX; x++) {
            if (object.visible(x, y))           
                object.draw();
            }
        }
    } 

The visible(x,y) method :

public boolean visible(int x, int y) {
    if (this.x == x && this.y == y) {
        return true;
    }
    return false;
} 

Now what happens is this :

Image 1. Character stands alongside the object, everything is ok.

Character stands alongside the object, everything is ok.

Image 2. Character triggers screen move left, half of the object gets drawn outside the map.

Character triggers screen move left, half of the object gets drawn outside the map.

Image 3. Character triggers screen move right, half of the object should be visible but its not.

Character triggers screen move right, half of the object should be visible but its not.

I know that this happens because I'm checking only if the upper left corner of the object is visible. I'm stuck. What should I do to remedy this?

Thank you for any ideas.

\$\endgroup\$
  • 1
    \$\begingroup\$ Is it really a big performance hit? If there's only a few of these objects, it's probably fine. Go make the rest of your game and come back if it's a big deal. \$\endgroup\$ – Vaughan Hilts Jul 29 '13 at 13:52
  • \$\begingroup\$ Your for loops seem pretty unnecessary. Can't you just have if (object.visible(firstX, lastX, firstY, lastY)) which then just does if (x >= firstX && x <= lastX && y >= firstY && y <= lastY)? In addition, your visible function could just be return <condition here>;. \$\endgroup\$ – Joseph Mansfield Jul 29 '13 at 18:18
1
\$\begingroup\$

Generally you don't store object positions in tiles. You can if the objects are small enough but as you found it gets tricky when they're larger.

Try storing the objects in a separate data structure like a quad tree. This way you can still quickly find all objects near the player without needing to loop over each tile position. Even for smaller objects, this has the potential of being much more efficient if your world is "sparse" (most tiles don't have objects).

Another option, if you want to stick with using the tile grid, is to delay drawing until after a sort and prune duplicates steps. Something like:

visible = empty array of objects

// find all visible objects, may include duplicates
for y in height:
  for x in width:
    add object at x,y to visible

// handle duplicates
sort visible
remove duplicates from visible

// draw the unique visible objects
for object in visible:
  draw object

You could also achieve the above with a simple flag.

// reset all objects
for object in world:
  object.checkDrawn = false

// draw unique objects in visible tiles
for y in HEIGHT:
  for x in WIDTH:
    if not object.checkDrawn:
      object.checkDrawn = true
      draw object

And yet another way would be to keep a frame number in each object (avoiding the need to "reset" the object list before rendering).

for y in HEIGHT:
  for x in WIDTH:
    if object.frame is not currentFrame:
      object.frame = currentFrame
      draw object

Still yet another way would be to take the array approach I list earlier and use a unique set (std::set in C++) data structure of some kind, which avoids the need to sort and prune duplicates as the set won't allow duplicates in the first place.

I do recommend the quad tree more, though. Use the tile grid for the tiles themselves and nothing else. Use separate data structures for separate purposes; tile grid for the map rendering and tile collision, quad tree for object rendering and collision, etc. You might even end up with separate data structures for object rendering vs collision, though you probably don't need it.

\$\endgroup\$
  • \$\begingroup\$ This seems like a very clean and logical solution. I will check out the quad tree. Thank you for your time. \$\endgroup\$ – dØd Jul 30 '13 at 5:40
1
\$\begingroup\$

There's a really simple solution to this if you just want to create a quick check, and draw if it's even remotely visible.

Construct a rectangle from your camera frustum and construct a rectangle from your tile. Make sure these are both in world cordinates. Then, simply check if they intersect. If they do, draw the object. If not, don't.

\$\endgroup\$
  • \$\begingroup\$ Hmm, I've never thought about doing a collision check with a camera lens for this problem. I will also try your approach, thank you for your time. \$\endgroup\$ – dØd Jul 30 '13 at 5:51
0
\$\begingroup\$

A possible solution is to have a 2 dimensional array in which you store which tile/object with which offset should be displayed for each world tile.

A other possible solution is to check against the tiles one step more left

for (int y = firstY; y < lastY; y++) {
    for (int x = firstX; x < lastX; x++) {
        if (object.visible(x, y))           
            object.draw();
        }
    }
} 

for (int y = firstY; y < lastY; y++) {
    if (object.visible(x-1, y))           
        object.draw(-1); // with offset
    }
}
\$\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.