3
\$\begingroup\$

I am creating an 2d bird view tile game using the pixijs render framework. Like pokemon or oldtime zelda.

In those games you sometimes walk behind buildings where the tile would be drawn on top of the player.

I am using an multidimensional array for each tile properties. Where you can set ground level images and level on top of ground level images.

The 'z-index' can be changed.

Should I create an new property to flag it as an tile that can be on top of the player? Where all those tiles with that property are drawn on top of the player. But there is an possibility that you can climb/walk on the building and the player should than be drawn on top of the tiles.

You should take the following into consideration

  • the game is an multiplayer game.

This could be an conflict if all the players/characters (the player,npc, cpu) are one layer and the tiles that can be drawn on top of the player are one layer. Because for example: one player is walking on top of the building and one is behind the building. You cant change the z-index of the player one lower. Because the player behind the building wil look like he is on top of it because the player is drawn on top of it by the changed z-index.

  • you can be drawn on top of the tile level (like walking om top of the building or mountain)

  • tiles can change (a building that is going to be destroyed for example) thus are not fixed

So how can I handle tiles on top of a player which can be also beneath one player but not the other?

\$\endgroup\$
  • \$\begingroup\$ It'd probably be better to give each tile a layer that never changes. Then, give the player a current layer, which can be changed by walking up and down stairs for example. \$\endgroup\$ – Ben Oct 8 '14 at 7:24
  • \$\begingroup\$ @Ben The z-index is created by rearranging the layers array. Performance based wil it be a huge disadvantages to rearrange all the tiles that are drawn above the player to set it beneath. And this for each tile you are walking above on. Especially when playing on online-multiplayer. The array changes constantly then \$\endgroup\$ – Ismail Oct 8 '14 at 10:02
  • \$\begingroup\$ Can't you just change when the player is drawn instead? \$\endgroup\$ – Ben Oct 8 '14 at 22:45
  • \$\begingroup\$ @Ben That is possible but you have to change the order for each tile to get the z-index when you are not using layers to group tiles. Which propably is an issue on performance \$\endgroup\$ – Ismail Oct 9 '14 at 16:36
1
+50
\$\begingroup\$

Draw all your tiles AND your sprites in the correct order:

  • z-index from bottom to toplevel
  • y-index from upper side to downside of screen
  • x-index from left to right

Note: The characters and tiles must be drawn in the same loop. That means a tile left, below or under the character must always drawn before the character.

You should also determine if the player is -within- a building and possibly not draw anything that is in the level above the player. So you can still see the player moving inside the building. This can be achieved by marking special tile types for the floor inside of buildings (= saves memory). If the player is now located at a special position you use the type of tile below to determine, if your player is within a building. If so, you only display tiles up to the current z-index of the players position. Alternatively, you can store this information on each map position (= needs more memory).

Some example code:

// X=0 is west end, X=MAXX-1 is east end
// Y=0 is north end, Y=MAXY-1 is south end
// Z=0 is downside, Z=MAXZ-1 is upside
#define MAXX 128
#define MAXY 128
#define MAXZ 4

// map stores tile ids for each
// position in pseudo-3d space
// 0 == no tile
// Actors Images are stored as tiles,
// so they get drawn at correct z-index
int map[MAXX][MAXY][MAXZ];

// All indoor tiles get an index >= 128
bool isIndoor(int tileid) {
  return (tileid >= 128); // return true
}

void drawTile(int rx, int ry, int z, int tileid) {
  // calculate isometric tile position on screen
  int screen_x = center_tile_x + rx*tile_size_x - z*z_shift_x;
  int screen_y = center_tile_y + ry*tile_size_y - z*z_shift_y;

  drawImage(screen_x, screen_y, tile_gfx[tileid]);
}


// we have max 15x15 tiles on screen
// 7 tiles north, west, east and south
// from tile in screen center...
int max_visible_dist_x = 7;
int max_visible_dist_y = 7;

// Function drawView draws Viewport
// having map coordinates x,y,z
// in the middle of the screen.
void drawView(int x, int y, int z) {
  int maxz;
  // if tile below current viewpoint center is an indoor tile
  // draw upto current "z" position only
  if (isIndoor(map[x][y][z-1])) maxz = z; else maxz = MAXZ; 

  for(int z=0; z<maxz; ++z) {
    for(int ry=-max_visible_dist_y; ry<=max_visible_dist_y; ++ry) {
      for(int rx=max_visible_dist_x; rx<=max_visible_dist_x; ++rx) {
        int tileid = map[rx+x][ry+y][z];
        if (tileid != 0) drawTile(rx, ry, z, tileid);
        // Note: Actors gets drawn as tile too!
        // If not: check each coordinate rx+x, ry+y, z
        // for identity with actor positions and 
        // call draw function when matching!
      }
    }
  }
}
| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

The solution is simple: draw all the tiles (and anything inside them, such as players) in top-down row order. You could of course do the same using z-index - anything on tile row X will have z-index greater than tile row X - 1.

I believe your conundrum is due to drawing all tiles first, then all players. I suspect you also simply use the Y coordinate as the z-index. This is reasonable if there is no elevation in your game, but as you've found, it causes issues when you have elevated tiles. Instead, you should draw tiles and their contents together, in top-down order or by doing the same using z-index.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

You can render the real-world tile depth to the z-buffer. When you render your characters, you can simply cull the pixels that fail the z-depth test, i.e. a tile closer to the camera is occluding the character.

For pure opaque/transparent tiles, the advantage is that you can do this per pixel, i.e. no sorting of tiles is necessary. If your tile graphics contain alpha, you can use that so that opaque parts have the depth rendered to the z-buffer, and (fully) transparent parts do not. Works for very basic tiles.

For tiles with alpha's strictly between 0 and 1, you still need to z-sort these with your characters, and quite possibly, this will still not be correct unless you resort to depth-peeling and other more advanced techniques.

| improve this answer | |
\$\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.