My city building game, WidgetCity, displays the city as a table, each cell of a table representing one tile.
The tile map is done purely using just CSS, and CSS classes. The map is also scrollable.
How is this all done?
Multiple approaches
As always, there is more than one approach to doing this type of a tile map.
The most obvious approach is to chop down each graphic in the game to one tile sized small images. Then, each of the small images is applied using a background image to each of the tiles. For example, an industrial zone is 3×3 tiles, so this would need 9 small images which each get applied to specific tiles to build the whole industrial zone.
This small image approach can be done in two ways: Using a CSS style to put the image as the background, or by inserting an img tag into the cell and changing its src.
There’s a lot of different graphics for just a single type of zone: Indstrial zones for example have different graphics for different land valued zones and different populations on the zone. The above approach would work, but it would require a lot of work to chop each big graphic into small ones and also each image would have to follow a specific naming scheme so it could be applied correctly.
Another approach is similar to what is used in CSS sprites: Each larger graphic is contained in a single file, and the image is then mapped into the small cells using CSS’ background-position.
The chosen approach
I decided to use the approach similar to CSS sprites. This is because it was simpler to work with the files, as I didn’t need to rename all the little images and define tons of CSS classes just to apply the specific backgrounds. This also somewhat simplified the JavaScript code applying the styles.
The game defines a set of CSS naming rules which are used in the stylesheets, so the code in the game can easily apply styles without having to use special cases all over the place. Each cell’s className attribute consists of several classes:
- An “offset class”, which is used to place the big image into the small cell: for example, “o31x1” is the bottom right corner of a 3×3 tile – o for offset, 3 for 3×3, 1×1 for the X and Y offset from the center of the tile. The game knows which position of the tile it’s laying out, so it simply adds the respective offset class into the cell’s className.
- The main “graphic class”, such as “indbuilding” for an industrial building.
- A “land value/population class”. This is used to choose which graphic of the building to actually display, such as a small rowhouse, or a big skyscraper. These look like v0p1, where v0 means land value 0 and p1 means population 1
There are also some other special classes:
- nopower class is applied to zone center cells if the zone doesn’t have power. This shows the little no power indicator
- For roads and powerlines, there are classes called up, right, down and left, which are applied in following cases: if you have a road, and there’s a section of road above it, the class “up” is given to the bottom road cell, and the class “down” is given to the upper. If there’s also a road to the left of the upper cell, it gets “left”. In the stylesheet, there are combinations of these four classes, which apply the correct road and powerline graphics to the cells.
The scripting side of things
Of course all this won’t work without JavaScript. Behind the scenes, the game stores the map as a two dimensional array of objects. The objects in the array contain various data, which is used to determine which classes to apply to each cell when “rendering” the map.
The classes are also applied only when something changes – the game keeps track of what classes are in the cells, as re-applying the class incurs a performance penalty.
What about canvas?
Lastly, some words on an alternative to using a table: the HTML5 canvas element.
While it may seem “wrong” to use table to do something like this, I think it’s in certain ways better than canvas. Firstly, you get better performance, and second, you need less code.
Using canvas, you would need to write the code to draw the correct parts of the graphics, and if you wanted animations like the game has now, you would need to write timing code to run the animations. So in addition to the performance penalty of using canvas rendering, you would need additional JavaScript code running the animations and everything else.
The benefits of using canvas would be that you could provide smooth scrolling. With a table it would be very tricky if not completely unfeasible. Canvas would also allow zooming or other effects.