Developing a WebGL Game – Part 3 (Maps)

For my turn based strategy game I envisaged players moving pieces over a map that would look similar to the Game of thrones intro. Unfortunately this would require awesome 3D modelling & graphics skills that are a little beyond what I currently possess but its something to aim for!

I tried a few different ways to generate a map for my game. Initially I tried using a couple of array’s to define where to create ground and the height the ground should be.

height: [
0, 0, 4, 1, 0, 0, 0, 0, 0, 0,
0, 0, 4, 1, 0, 0, 0, 0, 0, 0
],

groundType: [
0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 0, 0, 0, 0
]

I then iterated through the arrays creating blocks dependent on the height & ground type. You need to do this backwards otherwise you get the inverse map relative to the array declaration:

var y = WorldData.initParams.startY,
colPosition = WorldData.initParams.rowSize,
rowPosition = 1;

for (var i = WorldData.initParams.groundType.length - 1; i > -1; i--) {

if (WorldData.initParams.groundType[i] > 0) {

addBlock({
arrayIndex: i,
colPosition: colPosition,
rowPosition: rowPosition,
y: y,
blockHeight: WorldData.initParams.height[i] * WorldData.initParams.blockHeight
});
}

colPosition--;

//end of row
if (colPosition === 0) {
colPosition = WorldData.initParams.rowSize;
rowPosition++;
}
}

As I create each square of ground I assigned meta data to it to easily identify its location in the environment and type of terrain.

Below is a screenshot of a typical result (the white & red cubes represent creatures):

Blocks

Although I think this would work OK and allow me to manipulate the environment very easily (e.g. destroy a block of ground) it seemed very inefficient having a lot of cubes for a flat area of ground with surfaces that are never viewed. It also looked a bit Minecraft esq & not in such a good way so I started looking at other options!

Height and texture maps
I came across a cool post by Bjorn Sandvik who utilized realworld GIS data & three.js to produce a height and textured map of parts of Norway. Bjorn did a lot of work manipulating real world data  to produce a detailed texture & height map. This produced an awesome result but I don’t need something quite so complex.

At a high level my plan for creating terrain is:

  • Create a bitmap to represent the games map (this has the added advantage that I dont need to create a level editor)
  • Iterate through the bitmap’s pixels and assign a height value for each color (e.g. grey might represent mountains which are higher than trees)
  • Output a JavaScript array of this data
  • Use the array three.js to manipulate a plane’s vertexes to produce a height effect

Here is my very simple game bitmap enlarged (the original is 20 x 20 pixels):

Terrain PNG

I wrote a small C# program to iterate through the bitmap generate a JavaScript array string (note this is a slow way to do this and there is a faster way using LockBits method):

private string ParseBitmap(Bitmap bmp)
        {
            var sbHeightMap = new StringBuilder();

                for (var y = 0; y < bmp.Height; y++)
                {
                    for (var x = 0; x < bmp.Width; x++)
                    {
                        var clr = bmp.GetPixel(x, y);
                        var height = GetHeight(clr);
                        sbHeightMap.Append("," + height);
                    }
            }

            sbHeightMap.Remove(0, 1);

            return "[" + sbHeightMap + "]";
        }

I can then take the generated array and use it to manipulate a plane and then map the terrain image on top of it:

function loadNewTerrain() {

var geometry = new THREE.PlaneGeometry(WorldData.boardScreenWidth, WorldData.boardScreenWidth, WorldData.boardCells - 1, WorldData.boardCells - 1);

for (var i = 0, l = geometry.vertices.length-2; i++) {
geometry.vertices[i].z = WorldData.heightData[i];
}

var plane = new THREE.Mesh(geometry, WorldData.terrainTexture);

plane.material.side = THREE.DoubleSide;
plane.rotation.x = GameMath.de2ra(-90);
Game.newTerrain = plane;

Game.addObjectToScene(plane, 'ground');
}

This simple approach gives quite a pleasing affect with even a very simple image:

newTerrain

I tried this approach with a more complex 800×800 satellite photograph. The color to height mappings are not setup but it still produced a reasonable effect that shows the promise of this technique with a more detailed texture:

satelliteTexture

One issue this experiment brought up is how quickly game assets can get quite large – for example the height map of the above image is nearly 2mb and that I will need a good way of loading them hmm…

Advertisements