about
history
syntax
visu
arcade
goodies
wrappers
efficiency
examples
experiments
tabageos
tutorials
actiontad logo white
Complex isometric game Example using the com.actiontad.isometric.SimpleISOEngine

This example uses the SimpleISOEngine to make an isometric map with basic terrain
and places a ninja character in the scene that can be controlled with the keyboard.
Also there are some enemy spiders the ninja can interact with.

ISOTest.as

package
{
	import com.actiontad.basicGameEvents.GameObjectEvents;
	import com.actiontad.basicGameObjects.AnimationByBlit;
	import com.actiontad.basicGameObjects.BGOWithAnimations;
	import com.actiontad.basicGameObjects.LoopEventSubscriber;
	
	import com.actiontad.gameUtils.Tracer;
	import com.actiontad.gameUtils.TweenMath;
	import com.actiontad.gameUtils.BlitSpecs;
	
	import com.actiontad.isometric.SimpleISOEngine;
	import com.actiontad.isometric.ISOPoint;
	import com.actiontad.isometric.SimpleISOBox;
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	
	/**
	 * An advanced example of using the SimpleISOEngine. 
* Creates a basic scene and places a ninja in the scene that can be moved with the keyboard.
* The ninja will not pass through the tall things in the scene.
* There are also enemy spiders that will walk back and forth and cause hits with the ninja.
*
* The character images used in this example are from Reiner's tilesets - http://www.reinerstilesets.de/
* (96x96 bitmaps)
* they have been placed in sprite sheets with one row per animation starting at x0,y1 then skiping a row per animation,
* then scaled down to 64x64 with each image relatively in the middle of each grid square.
* the order of animations from top to bottom is ne, nw, se, sw *
* * The land images are from this isometric sprite sheet by Yar - http://opengameart.org/sites/default/files/iso-64x64-outside.png
* the land images sheet was used as is. (64x64) *
* (please see the licenses of the above artists before using those files for other than personal educational use) * */ [SWF(frameRate = '30', backgroundColor = '0x7d843d', width = '650', height = '400')] public class ISOTest extends SimpleISOEngine { //0 means no 3D height, flat, these values should be in intervals of the reactionHeight, by default that is 3 private var heightMap:Array = [ [6,6,3,3,3,3,3,3,3,3,3,3,6,0,0,0,0], [0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,6,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0], [0,0,0,0,0,3,3,3,0,0,0,3,6,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0], [0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,6,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0], [0,0,0,0,0,3,3,3,0,0,0,0,3,0,0,0,0], [3,3,0,3,3,3,3,3,3,3,3,3,6,0,0,0,0], [0,0,0,0,0,0,3,3,3,0,0,0,6,0,0,0,0], [0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,6,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0], [0,0,0,0,0,3,3,3,0,0,3,0,6,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0], [0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,6,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0], [0,0,0,0,0,3,3,3,0,0,0,0,3,0,0,0,0], [3,3,3,3,3,3,3,3,3,3,0,3,6,0,0,0,0], [0,0,0,0,0,0,3,3,3,0,0,0,6,0,0,0,0], [0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,6,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0], [0,0,0,0,0,3,3,3,0,0,3,0,6,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0], [0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,6,0,3,0,0,0,3,0,0,0,0], [0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0], [0,0,0,0,0,3,3,3,0,0,0,0,3,0,0,0,0], [3,3,3,3,3,3,3,3,3,3,3,3,6,0,0,0,0] ]; //0 means no skin from spriteSheet, which would show vector graphics instead. //Without any skin you can use the landTop and landSides properties to bitmpFill each SimpleISOBox. private var skinMap:Array = [ [8,8,8,8,8,8,8,8,8,8,8,8,8,1,1,1,1], [1,1,1,1,1,1,1,1,3,1,1,1,6,1,1,1,1], [1,1,1,1,3,1,15,1,3,1,1,1,5,1,1,1,1], [1,1,1,1,3,1,1,1,1,1,1,2,4,1,1,1,1], [1,1,1,1,1,3,3,3,1,1,1,9,4,1,1,1,1], [1,1,1,1,1,1,1,1,1,1,1,2,4,1,1,1,1], [1,1,1,1,1,1,1,1,3,1,1,1,5,1,1,1,1], [1,1,1,1,3,1,15,1,3,1,1,1,6,1,1,1,1], [1,1,1,1,3,1,1,1,1,1,1,1,7,1,1,1,1], [1,1,1,1,1,3,3,3,1,1,1,1,8,1,1,1,1], [8,3,1,3,3,3,3,3,3,3,3,3,8,1,1,1,1], [1,1,1,1,1,1,3,6,3,1,1,1,8,1,1,1,1], [1,1,1,1,1,1,1,1,3,1,1,1,6,1,1,1,1], [1,1,1,1,3,1,15,1,3,1,1,1,5,1,1,1,1], [1,1,1,1,3,1,1,1,1,1,2,1,4,1,1,1,1], [1,1,1,1,1,3,3,3,1,1,9,1,4,1,1,1,1], [1,1,1,1,1,1,1,1,1,1,2,1,4,1,1,1,1], [1,1,1,1,1,1,1,1,3,1,1,1,5,1,1,1,1], [1,1,1,1,3,1,15,1,3,1,1,1,6,1,1,1,1], [1,1,1,1,3,1,1,1,1,1,1,1,7,1,1,1,1], [1,1,1,1,1,3,3,3,1,1,1,1,8,1,1,1,1], [8,3,3,3,3,3,3,6,3,3,1,3,8,1,1,1,1], [1,1,1,1,1,1,3,3,3,1,1,1,8,1,1,1,1], [1,1,1,1,1,1,1,1,3,1,1,1,6,1,1,1,1], [1,1,1,1,3,1,15,1,3,1,1,1,5,1,1,1,1], [1,1,1,1,3,1,1,1,1,1,2,1,4,1,1,1,1], [1,1,1,1,1,3,3,3,1,1,9,1,4,1,1,1,1], [1,1,1,1,1,1,1,1,1,1,2,1,4,1,1,1,1], [1,1,1,1,1,1,1,1,3,1,1,1,5,1,1,1,1], [1,1,1,1,3,1,15,1,3,1,1,1,6,1,1,1,1], [1,1,1,1,3,1,1,1,1,1,1,1,7,1,1,1,1], [1,1,1,1,1,3,3,3,1,1,1,1,8,1,1,1,1], [8,8,8,8,8,8,8,8,8,8,8,8,8,1,1,1,1] ]; private var spiderSheet:BitmapData; private var spiders:Array = []; private var ani:Array = ["left", "right"]; private var hurtTime:Number = 0; public function ISOTest() { super(); this.graphics.beginFill(0x7d843d); this.graphics.drawRect(0, 0, 650, 400); } override public function subscribe(e:Event = null):void { super.subscribe(e); this.allowDuelControlMethods = false; this.userControlMethod = "k";//'k', 'm', 'keyboard', 'mouse' are all valid values this.movementType = "complex";//default is 'simple' spriteSheet = new LandSheet().bitmapData; //The numbers in this object corespond to the numbers in the skinMap and denote where to copyPixels from the spriteSheet. this.skinMapTilePoints = { 0:new Point(128, 0), 1:new Point(128, 0), 2:new Point(256, 512), 3:new Point(256, 704), 4:new Point(0, 384), 5:new Point(0, 384), 6:new Point(0, 384), 7:new Point(0, 384), 8:new Point(256, 320), 9:new Point(0, 640), 15:new Point(0, 384) }; this.spacing = 32;//spacing is a very important value, and changes the whole layout very much. this.makeMap(heightMap, skinMap, 64, 5, 64, 0x000000, 0x6495ed, 32); //The chars width hieght and length should be the same and should be about 5 more than the spacing. (if 5 is the default spaceOffset value of the char) //You will get the best value by trial and error, slight changes have a great effect on how well the char will move and react to the scenery. //the chars body is animated by the SimpleISOEngine, the spiders animation is created in this class var char:SimpleISOBox = new SimpleISOBox(null, null, 0, 0, 0, 36, 36, 36, 1, 0x000000, 0x00ff00, 0.00, new ISOPoint(1, 5, 0, this.spacing), 32); //In the AnimationByBlit constructor the false is the important value, without that the AnimationByBlit would move out of view. //also 64 and 64 are the width and height of the copyPixels operation for the AnimationByBlit. //also blank Points must be passed, not null, otherwise a default offset point is used. var ninjaImage:AnimationByBlit = new AnimationByBlit(null, new NinjaSheet().bitmapData, new BlitSpecs(64, 64, 1, 1, 1, new Point(), new Point(), false, true), this.globalDispatcher, null); //the ninja image is taken from its own spriteSheet, its walk images take up 4 rows and 8 columns starting at at y of 64 (1*64) and x of 0 (0*64) //the hurt images have 9 columns. ninjaImage.animationSpecs = { left:[2, [0, 1, 2, 3, 4, 5, 6, 7]], right:[3, [0, 1, 2, 3, 4, 5, 6, 7]], up:[1, [0, 1, 2, 3, 4, 5, 6, 7]], down:[4, [0, 1, 2, 3, 4, 5, 6, 7]], hurtleft:[7,[0,1,2,3,4,5,6,7,8]], hurtright:[8,[0,1,2,3,4,5,6,7,8]], hurtup:[6,[0,1,2,3,4,5,6,7,8]], hurtdown:[9,[0,1,2,3,4,5,6,7,8]] }; ninjaImage.changeToAnimation("hurtup"); ninjaImage.blitt(null); ninjaImage.oneWithAttached = true; char.addChild(ninjaImage); char.body = ninjaImage;//setting the body causes the SimpleISOEngine to change the animation of the body for different key presses in its captureKeys method. //This is always y -= (spacing*2) , and (spacing*2) should always be the BlitSpecs width and hieght of the chars body. //x -= spacing/2; ninjaImage.y -= 64; ninjaImage.x -= 32; container.addChild(char); //Because the char has a vector, at this time it is also assigned as the userControlledObject //Adding enemies and other things will be just like adding the userControlledObject (the char) var spider1:SimpleISOBox = spiderCreation(new ISOPoint(1, 9, 0, this.spacing)); var spider2:SimpleISOBox = spiderCreation(new ISOPoint(5, 2, 0, this.spacing)); var spider3:SimpleISOBox = spiderCreation(new ISOPoint(22, 1, 0, this.spacing)); this.ucoAnimationRate = 66.6 * 2; this.moveSceneBy( -25, -175);//the scene cannot be accessed directly, only via the moveSceneBy, moveSceneTo and scenePoints methods and property. this.bounceLevel = 0; this.autoScroll = true;//default is false this.scrollTweenInterval = 499.5;//default is 999 //this.scrollXOffset = -100; //this.scrollYOffset = 100; this.allowSpaceBarSceneControl = false;//default is true new Tracer(stage); } private function spiderCreation(spiderLocation:ISOPoint = null):SimpleISOBox { if (!spiderLocation) spiderLocation = new ISOPoint(0, 10, 0, this.spacing); var spider:SimpleISOBox = new SimpleISOBox(null, null, 0, 0, 0, 36, 36, 36, 1, 0x000000, 0xFF0000, 0.00, null, 32,this.globalDispatcher); if (!spiderSheet) spiderSheet = new SpiderSheet().bitmapData; var spiderImage:AnimationByBlit = new AnimationByBlit(null, spiderSheet, new BlitSpecs(64, 64, 1, 1, 1, new Point(), new Point(), false, true), this.globalDispatcher, null); spiderImage.animationSpecs = { left:[2, [0, 1, 2, 3, 4, 5, 6, 7]], right:[3, [0, 1, 2, 3, 4, 5, 6, 7]], up:[1, [0, 1, 2, 3, 4, 5, 6, 7]], down:[4, [0, 1, 2, 3, 4, 5, 6, 7]], attackleft:[7,[0,1,2,3,4,5,6,7,8]], attackright:[8,[0,1,2,3,4,5,6,7,8]], attackup:[6,[0,1,2,3,4,5,6,7,8]], attackdown:[9,[0,1,2,3,4,5,6,7,8]] }; spiderImage.changeToAnimation("down"); spiderImage.blitt(null); spiderImage.oneWithAttached = true; spider.addChild(spiderImage); spider.body = spiderImage; spiderImage.blittController.looperDelay = 133.2; spiderImage.y -= 64; spiderImage.x -= 32; container.addChild(spider); spider.vector = new ISOPoint(0, 0, 0, this.spacing); spider.location = spiderLocation; spider.body.changeToAnimation("right"); spider.addEventListener(GameObjectEvents.LOOP, spiderMove); spiders.push(spider); spiders.push(1); return spider; } private function spiderMove(e:Event):void { var spider:SimpleISOBox = e.target as SimpleISOBox; spider.vector.x = .01; var ai:int = spiders[spiders.indexOf(spider) + 1]; if (ai) { spider.location.add(spider.vector); } else { spider.location.subtract(spider.vector); } var np:ISOPoint; var tile:SimpleISOBox = gridChildAt(spider.location.x + (ai?1: -1), spider.location.y); if(tile && tile._h < 5) { np = spider.location.toScreen(true); spider.x = np.x; spider.y = np.y; } else { ai = spiders[spiders.indexOf(spider) + 1] = ai ? 0 : 1; spider.body.changeToAnimation(ani[ai]); tile = gridChildAt(spider.location.x, spider.location.y); } container.setChildIndex(spider, container.getChildIndex(tile) - 1); if ( SimpleISOEngine.proximityTest(spider, userControlledObject) ) { container.setChildIndex(spider, container.getChildIndex(userControlledObject) - 1); } //We use a tight pixel perfect proximity test because the images are offset somewhat from the center of the SimpleISOBoxes they are in, //and we do not want diagonal hit reactions in this case, to see the char and spider boxes, change 0.00 to 1 in the new SimpleISOBox calls in subscribe. if ( proximityTestPixelPerfect(spider, userControlledObject, false) ) { spider.removeEventListener(GameObjectEvents.LOOP, showPlayerHurt); spider.removeEventListener(GameObjectEvents.LOOP, spiderMove); hurtTime = 0; var ca:String = userControlledObject.body.currentAnimation; var sl:ISOPoint = spider.location.floor(true); var nl:ISOPoint = userControlledObject.location.floor(true); if (ca.indexOf("hurt") == -1) { this.userControlledObject.body.changeToAnimation("hurt" + ca); } if (nl.x <= sl.x && nl.y <= sl.y) { spider.body.changeToAnimation("attackdown"); } if (nl.x >= sl.x && nl.y > sl.y) { spider.body.changeToAnimation("attackup"); } if (nl.y == sl.y && nl.x <= sl.x) { spider.body.changeToAnimation("attackleft"); } if (nl.y == sl.y && nl.x > sl.x) { spider.body.changeToAnimation("attackright"); } //Tracer.reTrace(nl.x, nl.y, sl.y); spider.addEventListener(GameObjectEvents.LOOP, showPlayerHurt); } } private function showPlayerHurt(e:Event):void { hurtTime += this.looperDelay; var spider:SimpleISOBox = e.target as SimpleISOBox; //userControlledObject.body.blittController.looperDelay = ucoAnimationRate / 2; if(userControlledObject.body.currentAnimation.indexOf("hurt") != -1) { userControlledObject.body.blitt(null); } if (hurtTime >= 999) { spider.removeEventListener(GameObjectEvents.LOOP, showPlayerHurt); spider.addEventListener(GameObjectEvents.LOOP, spiderMove); spider.body.changeToAnimation(ani[spiders[spiders.indexOf(spider) + 1]]); this.userControlledObject.body.changeToAnimation(this.userControlledObject.body.currentAnimation.replace("hurt", "")); } } } }


The Result - SimpleISOEngine Isometric scene - click on it first for keyboard control


See the example in its own window here.










actiontad twitter.com/actiontad terms