1
\$\begingroup\$

I am working on a Tower defense style game on mobile that uses a tile map and I am trying to determine the best way to draw overlays of those tiles.

In my game, I go into build mode to build towers. When I am in build mode, the game determines for each tile, if a Tower can be placed on a certain tile. If the tile is empty, I draw a slightly transparent green image over the tile. If the tile already has a tower on it, or a tower just cant be built on a tile, I draw a slight transparent red image. like this:

Tile Overlays

Originally, when I was in build mode, I drew a overlay for every tile on the map, even if the tile was off screen. That killed performance so I came up with another solution.

I would only draw overlays for tiles that were on screen. So what I did was, for every frame,determine the top Left tile that the camera has in view, and determine the bottom right tile. Iterate through each tile and store it in a HashSet. I kept a member around for the last frame's hashset and then used ExceptWith on the current hashset to determine which tiles have came in/out of view. For each new tile, I would add a new RenderComponent for that tile, and for each old tile I would remove it.

 //snap the camera's position to a position offset of the grid
        Vector2 snappedCameraTopLeft = new Vector2((cameraLeftPosition.X / TileMap.TileWidth) * TileMap.TileWidth, (cameraLeftPosition.Y / TileMap.TileHeight) * TileMap.TileHeight);
        Vector2 snappedCameraBottomRight = new    Vector2((cameraRightPosition.X / TileMap.TileWidth) * TileMap.TileWidth, (cameraRightPosition.Y / TileMap.TileHeight) * TileMap.TileHeight);
        //Vector2 previousSnappedCameraTopLeft = new Vector2((mPreviousCameraLeft.X / TileMap.TileWidth) * TileMap.TileWidth, (mPreviousCameraLeft.Y / TileMap.TileHeight) * TileMap.TileHeight);
        //Vector2 previousSnappedCameraTopRight = new Vector2((mPreviousCameraRight.X / TileMap.TileWidth) * TileMap.TileWidth, (mPreviousCameraRight.Y / TileMap.TileHeight) * TileMap.TileHeight);

        ////snap the Camera's position to a specific tile
        TileCoord snappedLeft = GeometryOps.PositionToTileCoord(snappedCameraTopLeft);
        TileCoord snappedRight = GeometryOps.PositionToTileCoord(snappedCameraBottomRight);
        HashSet<Tile> previousTemp = new HashSet<Tile>(mPreviousTileSet);
        HashSet<Tile> previousTemp2 = new HashSet<Tile>(mPreviousTileSet);
        HashSet<Tile> addedTiles = new HashSet<Tile>();
        mVisibleTileSet = new HashSet<Tile>();
       //determine which tiles are in view of the camera
        for (int i = snappedLeft.Y; i < snappedRight.Y; i++)
        {
            for (int j = snappedLeft.X; j < snappedRight.X; j++)
            {
                TileNode tileNode = TileMap.GetTileNode(j, i);
                mVisibleTileSet.Add(tileNode.Tile);

                if (!mPreviousTileSet.Contains(tileNode.Tile))
                    addedTiles.Add(tileNode.Tile);

            }
        }
        //find all the tiles that have came into view/out of view
        previousTemp.ExceptWith(mVisibleTileSet);
        //determine which tiles have gone out of view, by excepting it with the added tiles
        previousTemp.ExceptWith(addedTiles);



        //remove the old renderComponents for tiles out of view
        foreach (Tile tile in previousTemp)
        {

            TileRender render = mTileRenders.Find(t => t.Position == tile.PositionComponent.Position);
            if (tile == null || render == null)
                continue;

            TileRenderPool.ReleaseObject(render);
            mTileRenders.Remove(render);

        }
        //add render components for newly added tiles
        foreach (Tile tile in addedTiles)
        {
            TileRender tilerndr = TileRenderPool.GetObject();

            tilerndr.Position = tile.PositionComponent.Position;

            if (!mTileRenders.Contains(tilerndr))
                mTileRenders.Add(tilerndr);
        }

This works ok when the game is idle, but its really slow when i start moving the camera around via touch gestures. Iterating ~800 tiles every frame and adding/removing overlays for ~150 tiles when using a flick gesture is really slow, and cuts my frame rate in half.

Is there a more efficient way I could be doing this? Another solution I tried was determine how much the camera has moved per frame, and only iterating the tiles between the camera's last position and camera's current position kinda worked, but I found it to be quite slow with gestures again as well.

\$\endgroup\$
  • \$\begingroup\$ The buttons are just as big as about 9 tiles. Should the player be able to click the tiles? Because if so, it would make sense to me to increase the size of your tiles (or zoom in, whatever). This would divide the amount of tiles rendered by about 9. The tiles seem too small to click the way they are now \$\endgroup\$ – Peethor Nov 22 '15 at 17:26
  • \$\begingroup\$ I forgot to mention that. Yes, you can zoom into the tiles by doing a pinch gesture. By zooming in, only about 1/4 of the tiles that you see on that image are visible. \$\endgroup\$ – iNFiNiTY Nov 22 '15 at 19:10
  • 1
    \$\begingroup\$ Depending on how exactly you want it to look, you could draw one green overlay that's just as big as the screen, and draw a fully opaque red overlay for each tile you can't build on. You would only need to know about those tiles, and only draw those. If you want the overlay to be semi-transparent, you would draw all tiles, then the green overlay, then the non-buildable tiles, and then the red overlays. Depending on how many buildings there are, you can really speed things up a lot \$\endgroup\$ – Peethor Nov 22 '15 at 20:34
  • \$\begingroup\$ I think this only partially solves the problem. Id still have to iterate through each tile and determine which tiles have buildings on them and activate/deactivate overlays for those tiles when they come in and out of view. Wont that just be as slow? \$\endgroup\$ – iNFiNiTY Nov 22 '15 at 21:00
  • 1
    \$\begingroup\$ I solved my issue via @Peethor's advice. I only kept track of the tiles that were occupied and drew a large green overlay over all the tiles, and drew smaller red tiles for all the occupied tiles \$\endgroup\$ – iNFiNiTY Nov 26 '15 at 19:54

Your Answer

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

Browse other questions tagged or ask your own question.