Grid based games – Part 5.1: Learning from the big ones

I know this may look like a call to clone. But the intention of this post is more like look and learn. Once you can do the same, make it better. We’ll have a look at Desktop Tower Defence and try to understand the basic methods of that game.
This post will cover the creation of the square grid in different colors and a method to obtain the actual position to build a tower.

Here is what we are going to create:

And here are the code snippets:

var nodeArray:Array = new Array();
var posArray:Array = new Array();
var rows = 24;
var cols = 28;
var size = 12;
var invSize = 1 / size;
var node:Object;
for ( var u = 0; u < rows; u++ )
{
	for ( var v = 0; v < cols; v++ )
	{
		node = new Object();
		node.v = v;
		node.u = u;
		node.nodePos = u + "." + v;
		node.x = v * size + 40;
		node.y = u * size + 40;
		nodeArray.push(node);
		posArray.push( u + "." + v );
	}
}

This creates a array of nodes with 24 rows and 28 columns. Each node gets assigned a position based on the size of a 12×12 pixel square plus 40 pixels for nice positioning on the stage.

var startNodes:Array = new Array("9.0", "10.0", "11.0", "12.0", "13.0", "14.0");
var endNodes:Array = new Array("9.27", "10.27", "11.27", "12.27", "13.27", "14.27");

We do know where to put start and end and thus fill to arrays with string of their position.

var map:MovieClip = new MovieClip();
addChild(map);

for each ( var nObj in nodeArray )
{
	if ( nObj.v == 0 || nObj.u == 0 || nObj.v == cols - 2 || nObj.u == rows - 2 )
	{
		nObj.blocked = true;
	}

Several nodes are not meant to be built on, so we block them.

	with ( map.graphics )
	{
		if ( startNodes.indexOf(nObj.nodePos) > -1 )
		{
			beginFill(0x00ff00, 0.5);
			lineStyle(1, 0xffffff, 0.1);
			//drawCircle(nObj.x, nObj.y, 1);
			drawRect( nObj.x - size * 0.5, nObj.y - size * 0.5, size, size );
			endFill();
		}

Start nodes get a green semitransparent fill.

		else if ( endNodes.indexOf(nObj.nodePos) > -1 )
		{
			beginFill(0xff0000, 0.5);
			lineStyle(1, 0xffffff, 0.1);
			//drawCircle(nObj.x, nObj.y, 1);
			drawRect( nObj.x - size * 0.5, nObj.y - size * 0.5, size, size );
			endFill();
		}

End nodes get a semitransparent red fill.

		else if ( nObj.v == 0 || nObj.u == 0 || nObj.v == cols - 1 || nObj.u == rows - 1 )
		{
			beginFill(0xffffff, 0.5);
			lineStyle(1, 0xffffff, 0.1);
			//drawCircle(nObj.x, nObj.y, 1);
			drawRect( nObj.x - size * 0.5, nObj.y - size * 0.5, size, size );
			endFill();
		}

Frame nodes get a semitransparent white fill.

		else
		{
			beginFill(0x000000, 0.5);
			lineStyle(1, 0xffffff, 0.1);
			//drawCircle(nObj.x, nObj.y, 1);
			drawRect( nObj.x - size * 0.5, nObj.y - size * 0.5, size, size );
			endFill();
		}

	}
}

All other nodes are filled semitransparent black.

var overlay:MovieClip = new MovieClip();
addChild(overlay);

The overlay will show the actual position once the mouse movement handler is set.

stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
function mouseMoveHandler ( event:MouseEvent )
{
	var actNode = getPos ( mouseX - size*0.5 , mouseY - size*0.5);
	with ( overlay.graphics )
	{
		clear();
		if ( actNode )
		{
			if ( actNode.blocked )
			{
				beginFill(0x990000, 0.5);
				lineStyle(1, 0x990000, 1);
				drawRect(actNode.x - size*0.5, actNode.y - size*0.5, size*2, size*2);
				endFill();
			}
			else
			{
				beginFill(0x009900, 0.5);
				lineStyle(1, 0x009900, 1);
				drawRect(actNode.x - size*0.5, actNode.y - size*0.5, size*2, size*2);
				endFill();
			}
		}
	}
}

The handler fires while the mouse moves. Through the getPos() function the actual node is returned. After a check of the blocked property a square is drawn to the overlay.

function getPos ( xPos, yPos )
{
	var thisV = Math.round (( xPos - 40 ) * invSize );
	var thisU = Math.round (( yPos - 40 ) * invSize );
	thisV == -1 ? thisV++: void;
	thisV == cols - 1 ? thisV--: void;
	thisU == -1 ? thisU++: void;
	thisU == rows - 1 ? thisU --: void;

	var nodeString = thisU + "." + thisV;
	var nodePos = posArray.indexOf(nodeString);
	if ( nodePos > -1 )
	{
		return ( nodeArray[nodePos] );
	}
}

With the actual x and y, the getPos function calculates the actual row and column the mouse is over. To not get nodes outside of the board area thisU and thisV are adapted to our actual grid. A string is built of the actual row and column. indexOf checks if that string exists in the posArray and returns the appropriate node.

Hmm, amazingly that’s pretty much it. Yoho!

This entry was posted in as3, flash, Grid Based Games, grids, mochiads and tagged , , , , , . Bookmark the permalink.
Be Sociable, Share!

3 Responses to Grid based games – Part 5.1: Learning from the big ones

  1. Pingback: Grid based Games – Part 5.2: Towers « YARR!cade.com LÞ the kegogrog blog

  2. Jokero says:

    invSize is not declared.

    invSize must be 1/12 or you can do:

    function getPos ( xPos, yPos )
    {
    	var thisV = Math.round (( xPos - 40 ) /size );
    	var thisU = Math.round (( yPos - 40 ) /size );
    	thisV == -1 ? thisV++: void;
    	thisV == cols - 1 ? thisV--: void;
    	thisU == -1 ? thisU++: void;
    	thisU == rows - 1 ? thisU --: void;
     	var nodeString = thisU + "." + thisV;
    	var nodePos = posArray.indexOf(nodeString);
    	if ( nodePos > -1 )
    	{
    		return ( nodeArray[nodePos] );
    	}
    }
    

    Greets

    • kegogrog says:

      Yep, forgot the line after

      var size = 12
      

      that just was

      var invSize = 1 / size
      

      I’ve learned that multiplication instead divsion is one step towards optimized code. So, it may not make a big difference in that case, but if you need some code that is executed a hundred times per frame you will look for those optimizations.

Leave a Reply

Your email address will not be published.