Tile layout in AS3 Page 1/1  
[ October 29, 2007 ] Eitan Avgil
How to make a tile based layout in AS3 using OOP architecture.

 

This article will show how to make a tile based layout in AS3, trying to keep OOP architecture.
The project can be compiled from pure code compiler (such as FlexBuilder, flashDevelop) or from a blank file in the flash IDE, pointing the main class from the stage.

The application will get a XML file containing map of number and will translate it to visual map on screen. This has no interaction – just the tile placement and manager.

The program will have X classes. Each class is responsible of doing specific things.

  1. main class – manage everything and create instances of all other classes
  2. XmlLoader – load an XML file and hold it's data
  3. Parser – parse the XML to rows array and cells in rows (each row is an Array)
  4. Screen – receive the array that the parser made, and build a matrix of tiles.
  5. Tile – a single tile; holds type, X position and Y position.

We will get to the Main class at the end.

First class – XmlLoader

package tools
{
	import flash.events.EventDispatcher;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	import flash.events.Event;
	
	public class XmlLoader extends EventDispatcher{
		
		private var _loader:URLLoader;
		
		private var _xml:XML;
		
		public function XmlLoader(path:String){
			_loader = new URLLoader();
			try{
				_loader.load(new URLRequest(path));
			}catch(e:Error){
				trace("error in loading the XML file")
			}
			_loader.addEventListener(Event.COMPLETE,onLoadXml);
		}
		
		private function onLoadXml(event:Event):void{
			//trace("xml loaded")
			_xml = XML(URLLoader(event.target).data)
			dispatchEvent(new Event(Event.COMPLETE));
		}	
		
		public function get xml():XML{
			return _xml;
		}
	}
}
	

This class receives path to the screen XML file, and tries to load it. When the XML is loaded, the class dispatches (shoot) an event so the class that created the instance of XmlLoader will know the loading process is done and that we can move on (this is a non synchronic operation that's why we need event mechanism).
Please notice that there is one event listening – to the loading operation, and a second event dispatching – so the main class will catch. The first is internal, and the second is to connect with other classes. After loading the XML data, the instance of XmlLoader will hold the data and enable other classes get it through a special getter function it has. Another thing to notice is the try & catch thing there – that handles special error events, and prevents an error message from being send to the user on runtime.

Second class – the parser

The parser's job is very simple – get a XML object and divide it to rows, and each row to single number. Again, after doing it's part, an instance of this class will hold an Array of rows so other classes will be able to read it. This too is done with a getter function. The _rows array is actually 2 dimension array. Every cell in it represent a single row, and every row has multiple digits in it, according to the XML line.

The constructor of the class receives a XML type. Hers how this class looks like:

package tools
{
	public class Parser{
		
		private var _xml:XML;
		private var _rows:Array;
		
		
		public function Parser(xml:XML){
			_xml = xml;
			_rows = new Array();
			getRows();
		}
		
		private function getRows():void{
			// get all rows
			var rows:XMLList = _xml.screen.children();
			var singleRow:Array;
			var rowString:String = rows[i];
			for(var i:uint=0;i<rows.length();i++){
				rowString= rows[i];
				// split numbers in string to cells in singleRow array 
				singleRow = rowString.split("");
				//save singleRow in _rows array;				
				_rows.push(singleRow);
			}	
		}
		
		//return an arra of all rows (every row is an array of numbers
		public function get rows():Array{
			return _rows;
		}
		
	}
}
    

In order for this thing to work, the XML file should have this structure:

<?xml version='1.0' encoding='UTF8'?>
<data>
	<screen id="" title="">
		<row>111111111111111111111111111111</row>
		<row>100000000000000000000000000001</row>
		<row>101000000000000000003300000001</row>
		<row>101000000000000000000000000001</row>
		<row>101111110000001111000000000001</row>
		<row>100000000000004400000000000001</row>
		<row>100000000000000000000111510001</row>
		<row>100000000000000000000000000001</row>
		<row>111111111111111111111111111111</row>
	</screen>
</data>
    

Any other structure will require a change in the parser.

Both Parser and XmlLoader are in a package (folder) called 'tools'.

Third class – a visual one – Screen

The Screen class instance is actually one screen. It receives an array of rows (the kind that the Parser parses to) and build a matrix of tiles from that array. It does it with 2 for loops. The first loop is to run over the lines, and the inner loop is to run within the line over all of the numbers in it. Each time the program is in the inner loop it creates a single tile, and pushes it to an array (so in the future we can get access to all tiles).

This class and the Tile class are in a package called 'visual'.

package visual
{
	import visual.Tile;
	import flash.display.Sprite;
	
	public class Screen extends Sprite{
		
		private var _screenArray:Array;
		private var _tiles:Array;
		
		public function Screen(screenArray:Array){
			_screenArray = screenArray;
			_tiles = new Array();
			var tile:Tile;
			var row:Array;
			
			// create matrix
			for(var i:uint=0;i<_screenArray.length;i++){
				row = _screenArray[i] as Array;
				for(var j:uint=0;j<row.length;j++){
					// create a new tile and push it to _tiles
					tile = new Tile(Number(row[j]),j,i)
					addChild(tile)
					_tiles.push(tile);
				}	
			}		
		}
		
		//let other classes get the tiles array. 
		public function get allTiles():Array{
			return _tiles;
		}
	}
}
    

Fourth class – Tile

The last visual class is the Tile class. It represent a single tile, and holds 3 properties of it:

  • Type (the code in the XML that represent this tile)
  • locationX – represent it's horizontal position in the matrix – numeral location – not pixels in the screen
  • locationY - represent it's vertical position in the matrix

All 3 properties have getter function, so outer objects can "ask" each tile for it's properties. I decided to represent the difference between the tiles type with a color change, though this can be easily changed.

This is how my Tile class look like :

package visual{
	import flash.display.Sprite;
	
	public class Tile extends Sprite{
		
		private var _type:uint;
		private var _locationX:uint;
		private var _locationY:uint;
		
		// colors of tiles
		static private var TILE_TYPES:Array = [0xDDDDDD,
                                               0xFF00AA,
                                               0xCC00CC,
                                               0xCCCC00,
                                               0x3300FF];
		private const TILE_SIZE:uint = 10;
			
		public function Tile(type:uint,locationX:uint,locationY:uint){
			// get parameters 
			_type = type;
			_locationX = locationX;
			_locationY = locationY;
			//draw a square 
			graphics.lineStyle(1,0x000000,0.2);
			// different color by type parameter 
			graphics.beginFill(	TILE_TYPES[_type],1);
			graphics.drawRect(0,0,TILE_SIZE,TILE_SIZE);
			graphics.endFill();
			x=TILE_SIZE*_locationX;
			y=TILE_SIZE*_locationY;
		}
		
		public function get type():uint{
			return _type;
		}
		public function get locationX():uint{
			return _locationX;
		}
		public function get locationY():uint{
			return _locationY;
		}
	}
}
    

The Main class

Finally – the main class. This is what makes the whole thing work:

package {
	import flash.display.Sprite;
	import tools.XmlLoader;
	import flash.events.Event;
	import tools.Parser;
	import visual.Screen

	public class TileGame extends Sprite{
		
		private var _xmlLoader:XmlLoader;
		private var _xmlData:XML;
		private var _screenParser:Parser;
		private var _screenArray:Array;
		private var _screen:Screen;
		
		
		public function TileGame(){
        	var url:String = "http://www.avgil.com/as3/tilegame/screen1.xml";
			try{
			_xmlLoader = new XmlLoader(url);
			} catch(e:Error){
				trace("couldn't load XML file")
			}
			// wait till the XmlLoader will finish loading the data 
			_xmlLoader.addEventListener(Event.COMPLETE,onXmlReady);			
		}
		
		private function onXmlReady(event:Event):void{
			//recieve the XML data from the event
			_xmlData = XmlLoader(event.target).xml as XML;
			//parse and build a screen from parsed data
			_screenParser = new Parser(_xmlData);
			_screenArray = _screenParser.rows;
			_screen = new Screen(_screenArray);
			_screen.x = 50;
			_screen.y = 50;
			addChild(_screen);
		}
	}
}
    

Notice that after the creation on the XmlLoader – the program waits for the XmlLoader to say "Ok – done" , and only then (onXmlReady function), it continue doing it's things.

Here's the Flex project packed in one zip file: TileGame.zip

 
 
Name: Eitan Avgil
Location: Israel
Age: 32
Flash experience: About 5 years experience in flash programming, 2 of them in OOP environment; about one year in AS3. Runs a flash community (in hebrew) called Flashoo and an AS3 blog.
Job: Team Leader of a small game development team
Website: http://www.avgil.com/
 
 
| Homepage | News | Games | Articles | Multiplayer Central | Reviews | Spotlight | Forums | Info | Links | Contact us | Advertise | Credits |

| www.smartfoxserver.com | www.gotoandplay.biz | www.openspace-engine.com |

gotoAndPlay() v 3.0.0 -- (c)2003-2008 gotoAndPlay() Team -- P.IVA 03121770048