SpriteSheet class for AS3Isolib

One of the requirements of my latest as3isolib project was to have “realistic” looks for all objects. The only way to achieve this is use bitmap assets so I paid a friend who’s really good at making animated 3d models to create animated assets for me and render them as PNG sprite sheets. Another thing I had to do was blit the sheets but there’s no built-in bitmap blitter class in as3isolib that I could use so I wrote the SpriteSheet class below.

package iso
{
	import de.polygonal.ds.Array2;
	import iso.Directions;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;

	/**
	 * Bitmap-blitter. The source must be a bitmap or sprite with 8 rows representing 8 directions.
	 * Each row contains an animation cycle where column 0 is for idle state.
	 * 
	 * @author Anggie Bratadinata
	 */
	public class SpriteSheet extends Sprite
	{
		//rendering interval
		public static const RENDER_INTERVAL:Number = 64;

		//bitmap frames
		protected var _bitmapArray:Array2;
		//sheet source
		protected var _source:DisplayObject;
		//frame size
		protected var _frameWidth:Number;
		protected var _frameHeight:Number;
		//the rendered bitmap
		protected var _frameBitmap:Bitmap;
		protected var _frameRect:Rectangle;

		//current facing direction
		protected var _direction:String;
		//current frame row
		protected var _currentRow:Array;
		protected var _currentColNum:Number;
		//render timer
		protected var _oldTime:Number = 0;
		protected var _renderInterval:Number;
		
		//is the south-facing sequence on the first row (true) or not (false)?
		protected var _southFirst:Boolean;
		
		//the visual state 
		protected var _isIdle:Boolean;
		
		//if true, a new bitmap will be generated
		protected var _isDirty:Boolean;

		public function SpriteSheet()
		{

		}

		/**
		 * Build the sprite sheet. The source must contain 8 rows of animation frames arranged in clockwise order.
		 * The first row can be the south face or the south west. A tool like SpriteForge renders tilesheets 
		 * with the south-facing sequence on the first row. 
		 * 
		 * Acceptable row arrangements : S,SW,W,NW,N,NE,E,SE or SW,W,NW,N,NE,E,SE,S
		 * 
		 * @param	source			The source Display object. 
		 * @param	frameWidth		animation frame width
		 * @param	frameHeight		animation frame height
		 * @param	renderInterval	delay between renders. This defines the animation blitting speed in milliseconds
		 * @param	southFirst		the south-facing sequence is on the first row (true) or the last (false). 
		 *                          
		 */
		public function build(source:DisplayObject, frameWidth:Number, frameHeight:Number, renderInterval:Number = SpriteSheet.RENDER_INTERVAL, southFirst:Boolean = true):void
		{
			
			_source = source;
			_frameWidth = frameWidth;
			_frameHeight = frameHeight;
			_renderInterval = renderInterval;
			_southFirst = southFirst;

			var sourceBd:BitmapData = new BitmapData(source.width, source.height, true, 0x00000000);
			sourceBd.draw(source, null, null, null, null, true);

			var numCols:Number = Math.floor(source.width / frameWidth);
			var numRows:Number = Math.floor(source.height / frameHeight);

			_frameRect = new Rectangle(0, 0, frameWidth, frameHeight);

			_bitmapArray = new Array2(numCols, numRows);

			for (var i:int = 0; i < numRows; i++)
			{
				for (var j:int = 0; j < numCols; j++)
				{
					var bd:BitmapData = new BitmapData(frameWidth, frameHeight, true, 0x000000);
					_frameRect.x = j * frameWidth;
					_frameRect.y = i * frameHeight;

					bd.copyPixels(sourceBd, _frameRect, new Point(0, 0));

					_bitmapArray.set(j, i, bd);

				}

			}
			
			_frameRect.x = 0;
			_frameRect.y = 0;
			
			//if the south faces is on the last row, move it to the first
			if (!_southFirst)
			{
				_bitmapArray.shiftDown();
			}

			setDirection(Directions.S);
			idle();

			_oldTime = getTimer();
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			
		}
		
		/**
		 * Set the state to idle
		 */
		public function idle():void
		{
			
			_isIdle = true;
			_isDirty = true;
		};
		
		/**
		 * Set the state to walk/animated
		 * 
		 */
		public function action():void
		{
			_isIdle = false;
			_isDirty = true;
		};

		protected function onEnterFrame(e:Event = null):void
		{

			var elapsed:Number = getTimer() - _oldTime;

			if (elapsed >= _renderInterval && _isDirty)
			{
				render();
				_oldTime = getTimer();
			}

		}

		protected function render():void
		{
			if (_frameBitmap == null)
			{
				_frameBitmap = new Bitmap(new BitmapData(_frameWidth, _frameHeight, true, 0x00000000));
				addChild(_frameBitmap);
			}

			_frameBitmap.bitmapData.lock();
			
			if (_isIdle)
			{
				_currentColNum = 0;
				_frameBitmap.bitmapData.copyPixels(_currentRow[_currentColNum], _frameRect, new Point(0, 0), null, null, false);
				//trace(this + "render idle");
				_isDirty = false;
			}
			else
			{
				
				if (_currentColNum < _currentRow.length-1)
				{
					try {
						_frameBitmap.bitmapData.copyPixels(_currentRow[_currentColNum], _frameRect, new Point(0, 0), null, null, false);
					}catch (error:Error) {
						//trace("ERROR RENDERING : " + _currentColNum);
						return;
					}
					_currentColNum++;
				}
				else
				{
					_currentColNum = 1;
				}
				
				_isDirty = true;
			}
			
			_frameBitmap.bitmapData.unlock();
		}

		/**
		 * Set the active direction and make _currentRow point to a specific row in _bitmapArray
		 * @param	direction
		 */
		public function setDirection(direction:String):void
		{
			//trace(this +"set direction : " +direction);
			_direction = direction;
			var rowNum:Number;
			switch (direction)
			{
				case Directions.S:
					rowNum = 0;
					break;
				case Directions.SW:
					rowNum = 1;
					break;
				case Directions.W:
					rowNum = 2;
					break;
				case Directions.NW:
					rowNum = 3;
					break;
				case Directions.N:
					rowNum = 4;
					break;
				case Directions.NE:
					rowNum = 5;
					break;
				case Directions.E:
					rowNum = 6;
					break;
				case Directions.SE:
					rowNum = 7;
					break;
				default:
					rowNum = 0;
			}
			//trace(this + "setDirection, row " + rowNum);
			_currentRow = _bitmapArray.getRow(rowNum);
			
			_isDirty = true;
			render();
			
		}
		
		/**
		 * Get currently rendered frame
		 * 
		 * @return Bitmap
		 */
		public function getFrameBitmap():Bitmap
		{
			return _frameBitmap;
		}
		
		/**
		 * Get current direction
		 * @return
		 */
		public function getDirection():String
		{
			return _direction;
		};

	}

}

Next, I'll explain how to create your sheets.

Continue reading →

Helping client find a web host

So you have done a flash website for your client. You put all the necessary files on your server for demoing your hard work to them. They like it very much and think that you’re the best coder out there.

Now they want your help to set up a server for the website too because it is the first website they ever had and they don’t have a clue on how and where to host their shiny, brand new website. They are not really tech-savvy so telling them to google for web hosts won’t do any good for them. They might even get confused by thousands of web hosting sites Google gives them. Of course, you can go to authoritative forums like webhostingtalk.com to help them find a good host but it’s going to cost you some time. Oh and the client think that you can do this for free because you’re a rock star coder and you can do it in just a few minutes.

I had been in the same situation many times. The best places for me and my clients to look for solid webhosts and compare them easily are webhosting review sites. A review site that I just recently found is Web Hosting Search. This review site provides quick links to find hosts along with their ratings by the operating systems. So if your client wants Mac, you can just go to Mac Web Hosting or Windows Web Hosting if they want Windows.

Webhostingsearch is more than just a review site. It also contains lots of useful informations for people who don’t know anything about web hosting or the web in general. So your client will not only be able to choose a web host that fits their site’s requirements and their budget but they can also learn a few things about the web like how to set up a website, what web hosting is all about, etc. So whether it is a Mac Web Hosting or a Windows Web Hosting, they have it!

So, by directing your client to Webhostingsearch, you not only save your time and theirs but also help them to learn more about the web.

Good luck!

Creating Drag-and-Drop for AS3 Isolib

Some folks had asked about drag-and-drop mechanism in as3isolib discussion group. While there’s no such feature built-in in the library, it’s not hard to write it yourself.

So here’s a super simple class that provides that mechanism, called DragManager. I borrowed the idea from Flex’s DragManager. Note that this class is really a simple one and it doesn’t respect “grid” so you’ll have to modify it if you want to use it in a grid-based isometric space. Trust me, it’s not too hard 😉

Click to see the demo

Continue reading →

Beli ebook murah di O’Reilly

Minggu lalu ada promo gila di O’reilly. 1 ebook, judul apapun, diobral $10. Jadi deh beli 4 ebook.

Lucunya karena promo ini server mereka overloaded jadi ada order yg ga langsung muncul di account. Yang muncul juga ga bisa diunduh.

O'Reilly ebooks

Lumayan, $40 dapet 4 buku. Kalo ga pas promo, harga totalnya bisa lebih dari $100.