Terrain Modification in Grid Based Games – Appendix B: Shading

Terrain Modification Tutorial Thumbnail

Alright, giving the landscape some natural feeling by applying colors to the tiles. This simply fits into the dynamic drawing of tiles by using a fill. The shading here will be done with static colors instead of shadow calculation.

The result will look like this:

First things first. When it comes to choosing a color palette that fits the needs of our application. The most simple thing I could think of here was shades of green with blue for the zero level (aka water). The surface of planet could be red, a desert maybe yellow and white could be used for the inside of a refridgerator (could be an interesting game).

The shading main palette for the tiles

The shading main palette for the tiles

The next thing I did was to set a tile type. That way I was able to use switch...case (more on that here). The next code is based on Appendix A. There, right after the makeTiles function I inserted the following.

function recalcTiles()
{
	for each ( var tile in tileArray )
	{
		tile.low = tile.n.w;
		tile.e.w < tile.n.w ? tile.low = tile.e.w : void;
		tile.s.w < tile.e.w ? tile.low = tile.s.w : void;
		tile.w.w < tile.s.w ? tile.low = tile.w.w : void;
		
		tile.n.i = tile.n.w - tile.low;
		tile.e.i = tile.e.w - tile.low;
		tile.s.i = tile.s.w - tile.low;
		tile.w.i = tile.w.w - tile.low;
		
		tile.tType:String = tile.n.i.toString()+tile.e.i.toString()+tile.s.i.toString()+tile.w.i.toString();
	}
}

The lowest height value (sic!) is found by comparing all four corner nodes. Each node then gets assigned a value i determining his difference from the lowest. Those values are hopefully between 1 and 0. A string is created in clockwise order.

Remember that drawTile function? That's the subject of change now.

function drawTiles()
{
	
	var tileMapSizeH = ( nodeCols - 1 + nodeRows - 1 ) * tileSizeH;
	var tileMapSizeV = ( nodeRows - 1 + nodeCols - 1 ) * tileSizeV;
	tileMap.x = stage.stageWidth * 0.5;
	tileMap.y = stage.stageHeight * 0.5 - tileMapSizeV * 0.5;
	tileMap.graphics.lineStyle(1, 0x000000);
	
	for each ( var tile in tileArray )
	{
		if ( tile.valid )
		{
			var tileText = new TextField();
			tileText.text = String(""+tile.n.w+tile.e.w+tile.s.w+tile.w.w);
			tileText.autoSize = TextFieldAutoSize.CENTER;
			tileText.x = tile.s.xPos + tileMap.x - tileText.width/2;
			tileText.y = tile.s.yPos + tileMap.y;
			addChild(tileText);
			
			tile.n.zPos == tile.s.zPos ? tile.ver = true : tile.ver = false;
			tile.e.zPos == tile.w.zPos ? tile.hor = true : tile.hor = false;
			
			drawFour(tile);
		}	
	}
	addChild(tileMap);
}

Besides the text field (just information) the whole drawing code is in an external function.

function drawFour(tile)
{
	with ( tileMap.graphics )
	{
		switch(tile.tType)
		{
			case "0000":
				//flat
				moveTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				if ( tile.lev == 0 )
				{
					beginFill(0x000099);
				}
				else
				{
					beginFill(0x009900);
				}
				lineTo(tile.e.xPos, tile.e.yPos - tile.e.zPos);
				lineTo(tile.s.xPos, tile.s.yPos - tile.s.zPos);
				lineTo(tile.w.xPos, tile.w.yPos - tile.w.zPos);
				lineTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				endFill();
				break;
			case "0001":
				//north
				moveTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				beginFill(0x003300);
				lineTo(tile.e.xPos, tile.e.yPos - tile.e.zPos);
				lineTo(tile.w.xPos, tile.w.yPos - tile.w.zPos);
				lineTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				endFill();
				//south
				moveTo(tile.e.xPos, tile.e.yPos - tile.e.zPos);
				beginFill(0x006600);
				lineTo(tile.s.xPos, tile.s.yPos - tile.s.zPos);
				lineTo(tile.w.xPos, tile.w.yPos - tile.w.zPos);
				lineTo(tile.e.xPos, tile.e.yPos - tile.e.zPos);
				endFill();
				break;
			case "0010":
				//east
				moveTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				beginFill(0x003300);
				lineTo(tile.e.xPos, tile.e.yPos - tile.e.zPos);
				lineTo(tile.s.xPos, tile.s.yPos - tile.s.zPos);
				lineTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				endFill();
				//west
				moveTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				beginFill(0x00CC00);
				lineTo(tile.s.xPos, tile.s.yPos - tile.s.zPos);
				lineTo(tile.w.xPos, tile.w.yPos - tile.w.zPos);
				lineTo(tile.n.xPos, tile.n.yPos - tile.n.zPos);
				endFill();
				break;
//and so on

Okay, so I had a look at every tile (like they used to look in Appendix A) and determined the direction of its parts. Then, with a look at the chosen color palette and beginFill and endFill I brought them to life. There is a case setting for each of the 16 tiles. To be honest there are only 15, because both flat tiles are in the same case, and their color just depends on their level. Seeing that concept I am sure you can figure out the rest of that function above on yourself.

After applying that the showroom will look like that.

EDIT: Here's the Source (zip).

Yoho!

This entry was posted in as3, flash, game development, grids, mochiads, Terrain Modification, Tutorial and tagged , , , , , . Bookmark the permalink.
Be Sociable, Share!

5 Responses to Terrain Modification in Grid Based Games – Appendix B: Shading

  1. Chris says:

    Everything works awesome except for two errors I found.

    One:
    tile.tType:String = tile.n.i.toString()+tile.e.i.toString()+tile.s.i.toString()+tile.w.i.toString();
    Fixed by changing to:
    tile.tType = tile.n.i.toString()+tile.e.i.toString()+tile.s.i.toString()+tile.w.i.toString();

    Two:
    1120: Access of undefined property tileMap.
    On lines:
    138’ish: tileMap.x = stage.stageWidth * 0.5;
    139’ish: tileMap.y = stage.stageHeight * 0.5 – tileMapSizeV * 0.5;
    140’ish: tileMap.graphics.lineStyle(1, 0x000000);

    149’ish: tileText.x = tile.s.xPos + tileMap.x – tileText.width/2;
    150’ish: tileText.y = tile.s.yPos + tileMap.y;

    159’ish: addChild(tileMap);

    164’ish: with ( tileMap.graphics )

    These ones I don’t know how to fix? Please help!

    • kegogrog says:

      First one: Excellent find! I found that I had changed that in the fla already.
      Second one: Did you declare the var tileMap:Sprite = new Sprite(); before the function?

      Added the source to the article. Maybe you can find what’s missing by comparison.

      • Chris says:

        I’ll have to have another look at it when I get a chance. I have a lot of spare time at the moment so I thought I’d have a go at making a Flash/Facebook kind of game (like FarmVille, Backyard Monsters, etc) turns out that even though I have a great knowledge of web programming (PHP, ASP, Perl, MySQL, etc) it’s not enough.

        Trying to learn flash, and probley going the wrong way about everything!

        I just came up with a tile-map by myself that uses a movieclip as the tile (repeated several hundred times), then I made it isometric.

        But I couldn’t center it easily on the stage, then had some other issues.. Almost have it looking like what I want, but not sure I can actually use it for a game, lol.

        Wrecking my brain aye, so much goes into the little things here.

      • kegogrog says:

        Don’t get lost in the big project. Learning by doing is in my opinion the best way to learn flash, but you get easily frustrated if there is too less progress towards a big accomplishment.

        I wish I had some spare time (going foreward in making the map and it’s buildings and inhabitants in 3D!) but if you encounter problems that slow you down, send me a mail.

  2. Chris says:

    Will do mate.

    As it is, if I ever get anything working and into a playable state. You’ll be atop of the credits.. Very, very good information you have supplied us with here.

    I think I’ve almost read everything you’ve written, and got a little addicted to your Flu games too.

Leave a Reply

Your email address will not be published.