6
\$\begingroup\$

I am fairly new to this world, so bear with me.

I have a grid of blocks in a top-down, 2d game written using Slick. The best way to think of it is the Pokemon world.

Right now, I am rendering all the blocks on a specific map, whether they are on the screen at the time or not.

Is there a better way to do this? My thought was maybe to try creating an image out of what I would have rendered and taking the section that fits the screen to render. I don't know how to do this, but I wanted to consult the experts before I dove into it. Thanks! ;)

EDIT

Just thought I would add that Slick has a way of dealing with clipping in specialized cases as well as the screen's viewport. See the Wiki page for more info.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Yes, you should only render what is on the screen. Perhaps a little buffer on the outside as well so you don't get a flicker along the edge if the player moves fast. I don't know if java/slick provides a built in way to do this though. I would imagine they do. \$\endgroup\$
    – user159
    Commented Sep 4, 2011 at 16:07

3 Answers 3

11
\$\begingroup\$

One way to do this is to check the bounding box of the block against the bounding box of the current view (plus a bit for safety).

If the block interacts with the view (either fully inside or clipped) then draw it. If it doesn't - i.e. is totally outside then don't draw.

This check can be performed quite quickly - particularly in 2D. You don't have to know how much of the block is visible - just that some part of it is potentially visible.

If the maximum X coordinate of the block is less than the minimum X coordinate of the view or the minimum X coordinate of the block is greater than the maximum X coordinate of the view then this block is definitely invisible. The same test can be done on the Y coordinate.

As Joe points out in is comment the framework/libraries you are using might have this functionality built in.

\$\endgroup\$
2
\$\begingroup\$

Do something like this:

if (renderX >= 0 && renderY >= 0
    && renderX <= container.getWidth() && renderY <= container.getWidth()) {
    //Render
}

It will have to be custom for each object since every object has a different size. I render each group of components separately. If you're using TiLeD, here's a nice piece of code I made:

TiledMap map = mapComp.getMap();

    //Render positions as integers
    int renderX = (int) renderPos.getX();
    int renderY = (int) renderPos.getY();

    //Render offset for map
    int xRenderOffset = renderX % 32;
    int yRenderOffset = renderY % 32;

    //Get the first tile to be rendered
    int firstTileX = (int) -renderX / 32;
    int firstTileY = (int) -renderY / 32;

    //Render tiles only if their indices are greater than 0
    if (firstTileX < 0) {
        xRenderOffset += Math.abs(firstTileX) * 32;
        firstTileX = 0;
    }

    if (firstTileY < 0) {
        yRenderOffset += Math.abs(firstTileY) * 32;
        firstTileY = 0;
    }

    //Get the last tile to be rendered
    int lastTileX = firstTileX + (client.getContainer().getWidth() / 32) + 1;
    int lastTileY = firstTileY + (client.getContainer().getHeight() / 32) + 2;

    //Verify that the last tile is valid
    if (lastTileX > 127) {
        lastTileX = 127;
    }

    if (lastTileY > 127) {
        lastTileY = 127;
    }

    //Check if the map will be visible on the screen
    if (lastTileX >= 0 && lastTileY >= 0) {
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 0, true);
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 1, true);
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 2, true);
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 3, true);
    }

32 is your tile width, and 127 is your map size - 1.

\$\endgroup\$
1
  • \$\begingroup\$ Nice bit of code, but I'm not going with TiLeD on this one. I have decided to roll my own way of handling maps that I am enjoying quite well. It's based off the text file implementation of the Platformer starter kit put out by Microsoft. +1. :) \$\endgroup\$
    – Andy
    Commented Oct 3, 2011 at 0:45
1
\$\begingroup\$

If your blocks are uniformly sized (as is the case in the classic Pokemon games) and stored in a 2d array, then probably the best way to do this is to just do for-loops, something like this:

int startX = playerX - halfWidth;
int startY = playerY - halfHeight;

for( int x = startX , maxX = playerX + halfWidth; x < maxX; ++x )
{
    for( int y = startY , maxY = playerY + halfHeight; y < maxY; ++y )
    {
        DrawBlock( x - startX, y - startY, tiles[x][y] );
    }
}

where halfWidth and halfHeight are half the width and height of the screen respectively. This will cut the calls to DrawBlock down to only those that are visible. If you are scrolling around the grid, then you can draw an extra tile in each direction and add your animated offset values to the DrawBlock parameters as needed.

It should be mentioned that this method is complementary to the method of clipping that you linked to in your edit. Using the method I've presented here, clipping would be useful if your blocks shouldn't take up the entire screen (e.g. there is a border around them for a User Interface), to prevent extra blocks that are being drawn for animation purposes from being drawn over other elements. In this case, you would set your clipping region to be exactly the space where you want your grid to be displayed, and then draw only the minimal blocks necessary.

\$\endgroup\$
1
  • \$\begingroup\$ good recommendation! it does seem that Slick handles these things gracefully in the background, though, so adding my own code to deal with clipping won't be necessary. \$\endgroup\$
    – Andy
    Commented Sep 5, 2011 at 19:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .