Last active
December 15, 2016 15:31
-
-
Save payamsbr/4575067be0f51bdd6a2a5211f4000f31 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import flash.events.Event; | |
import flash.display.Loader; | |
import flash.net.URLRequest; | |
import flash.display.BitmapData; | |
import flash.display.Sprite; | |
import flash.display.Bitmap; | |
import flash.geom.Matrix; | |
import flash.display.MovieClip; | |
import flash.geom.Rectangle; | |
import flash.events.KeyboardEvent; | |
const src_grass_tile_url:String = "https://i.stack.imgur.com/sjJFS.png"; | |
const src_character_atlas_url:String = "https://i.stack.imgur.com/B7ztZ.png"; | |
var loader:Loader = new Loader(); | |
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, setGround); | |
loader.load(new URLRequest(src_grass_tile_url)); | |
// initialize game Loop function for updating game objects | |
addEventListener(Event.EXIT_FRAME, loop); | |
function setGround(e:Event):void { | |
/* drawing ground */ | |
/* loader is a displayObject, so we can simply draw it into the bitmap data*/ | |
/* create an instance of Bitmapdata with same width and height as our window*/ | |
/* (also set transparent to false because grass image, does not contains any transparent pixel) */ | |
var grass_bmd:BitmapData = new BitmapData(loader.width, loader.height, false, 0x0); | |
/* time to draw */ | |
grass_bmd.draw(loader); // drawing loader into the bitmapData | |
/* now we have to draw a tiled version of grass_bmd inside a displayObject Sprite to displaying | |
BitmapData on stage */ | |
var grass_sprite:Sprite = new Sprite(); | |
// for drawing a bitmap inside sprite, we must use <beginBitmapFill> with graphic property of the sprite | |
// then draw a full size rectangle with that Fill-Data | |
// there is a repeat mode argument with true default value so we dont set it true again. | |
// use a matrix for scalling grass Image during draw to be more cute! | |
var mx:Matrix = new Matrix(); | |
mx.scale(2, 2); | |
grass_sprite.graphics.beginBitmapFill(grass_bmd, mx); | |
grass_sprite.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); | |
// now add sprite to displayobjectcontainer to be displayed | |
stage.addChild(grass_sprite); | |
// well done, ground is ready, now we must initialize our character | |
// first, load its data, i just re-use my loader for loading new image, but with another complete handler (setCharacter) | |
// so remove existing handler, then add new one | |
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, setGround); | |
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, setCharacter); | |
loader.load(new URLRequest(src_character_atlas_url)); | |
} | |
function setCharacter(e:Event):void { | |
// let assuming that what is really character! | |
// a set of images inside a single image! | |
// that images are frames of our character (also provides movement for different directions) | |
// first load this | |
var character_bmd:BitmapData = new BitmapData(loader.width, loader.height, true, 0x0); // note character is transparent | |
character_bmd.draw(loader); | |
// take a look at sprite sheet, how many frames you see? | |
// 42 frames, so we can get width of a single frame | |
const frame_width:uint = character_bmd.width / 42; // 41 pixels | |
// as i show you above, to displaying a BitmapData, we have to draw it using a DisplayObject, | |
// another way is creating a Bitmap and setting its bitmapdata | |
var character_bmp:Bitmap = new Bitmap(character_bmd); | |
// but its not enough yet, a movieClip is necessary to cliping and animating this bitmap (as a child of itself) | |
var character_mc:MovieClip = new MovieClip(); | |
character_mc.addChild(character_bmp); | |
character_bmp.name = "sprite_sheet"; // setting a name to character_bmp, for future accessing | |
character_mc.scrollRect = new Rectangle(0, 0, frame_width, character_bmd.height); // cliping movieclip, to dusplaying only one frame | |
character_mc.name = "character"; // setting a name to character_mc, for future accessing | |
stage.addChild(character_mc); // adding it to stage. | |
// well done, we have a character, but its static yet! 2 steps remaining. 1 controlling 2 animating | |
// at first setting a control handler for moving character in 8 directions. | |
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown); | |
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp); | |
} | |
// we storing key stats inside <keys> Object | |
var keys:Object = {u:false, d:false, l:false, r:false}; | |
function keyDown(e:KeyboardEvent):void { | |
switch (e.keyCode) { | |
case 38: //up | |
keys.u = true; | |
break; | |
case 40: //down | |
keys.d = true; | |
break; | |
case 37: //left | |
keys.l = true; | |
break; | |
case 39: //right | |
keys.r = true; | |
break; | |
} | |
} | |
function keyUp(e:KeyboardEvent):void { | |
switch (e.keyCode) { | |
case 38: //up | |
keys.u = false; | |
break; | |
case 40: //down | |
keys.d = false; | |
break; | |
case 37: //left | |
keys.l = false; | |
break; | |
case 39: //right | |
keys.r = false; | |
break; | |
} | |
} | |
// speed of character movement | |
const speed:Number = 8; | |
var last_keyStat:Object = {u:false, d:false, l:false, r:false}; // used to getting a backup of previous keyboard stat for detecting correct idle direction | |
// this function will be called on each frame, with same rate as your project fps | |
function loop(e:Event):void { | |
if (keys.u) stage.getChildByName("character").y -= speed; | |
else if (keys.d) stage.getChildByName("character").y += speed; | |
if (keys.l) stage.getChildByName("character").x -= speed; | |
else if (keys.r) stage.getChildByName("character").x += speed; | |
// animation detection | |
if (keys.u && keys.l) { animate(keyframs.run.up_right); flip(true); } | |
else if (keys.u && keys.r) { animate(keyframs.run.up_right); flip(false); } | |
else if (keys.d && keys.l) { animate(keyframs.run.down_right); flip(true); } | |
else if (keys.d && keys.r) { animate(keyframs.run.down_right); flip(false); } | |
else if (keys.u) { animate(keyframs.run.up); flip(false); } | |
else if (keys.d) { animate(keyframs.run.down); flip(false); } | |
else if (keys.l) { animate(keyframs.run.right); flip(true); } | |
else if (keys.r) { animate(keyframs.run.right); flip(false); } | |
else { | |
// if character dont move, so play idle animation | |
// what is the best practice to detecting idle direction? | |
// my suggestion is to sotring previous keyboard stats to determining which idle direction is correct | |
// do any better thing if possible (absolutely is possible) | |
// i just simply copy it from above, and replaced (keys) with (last_keyStat) and (run) with (idle) | |
if (last_keyStat.u && last_keyStat.l) { animate(keyframs.idle.up_right); flip(true); } | |
else if (last_keyStat.u && last_keyStat.r) { animate(keyframs.idle.up_right); flip(false); } | |
else if (last_keyStat.d && last_keyStat.l) { animate(keyframs.idle.down_right); flip(true); } | |
else if (last_keyStat.d && last_keyStat.r) { animate(keyframs.idle.down_right); flip(false); } | |
else if (last_keyStat.u) { animate(keyframs.idle.up); flip(false); } | |
else if (last_keyStat.d) { animate(keyframs.idle.down); flip(false); } | |
else if (last_keyStat.l) { animate(keyframs.idle.right); flip(true); } | |
else if (last_keyStat.r) { animate(keyframs.idle.right); flip(false); } | |
} | |
// update last_keyStat backup | |
last_keyStat.u = keys.u; | |
last_keyStat.d = keys.d; | |
last_keyStat.l = keys.l; | |
last_keyStat.r = keys.r; | |
} | |
// well Done!!! only one step remaining, animation, the most important part of this tutorial | |
// what is really animation? a set of keyframes which contains atleast one frame | |
// lets creating our keyframs Object, which contains name of keyframes | |
// and also some data about starting and ending frame of this keyframe | |
// Note, in isometric games, each keyframe contains 8 direction (can be reduced to 5 with use of flipping) | |
var keyframs:Object = { | |
idle: {up:[0,0], up_right:[1,1], right:[2,2], down_right:[3,3], down:[4,4]}, // [2,2] means start frame is 2 and end frame is 2 | |
run: {up:[5,10], up_right:[11,16], right:[17,22], down_right:[23,28], down:[29,34]} | |
}; | |
// we should ignore remaining frames, this example only provides idle and run animation | |
// for example the start frame of idle animation with direction right, is: <keyframs.idle.right[0]> | |
// now lets implementing Animator function | |
var current_frame:uint; | |
function animate(keyframe:Array):void { | |
// how it works | |
// just called with a keyframe with direction (each frame), | |
// if keyframe is what is already playing, its just moved to next frame and got updated (or begning frame for loop) | |
// other wise, just moved to begining frame of new keyframe | |
if (current_frame >= keyframe[0] && current_frame <= keyframe[1]) { // check if in bound | |
current_frame++; | |
if (current_frame > keyframe[1]) // play back if reached | |
current_frame = keyframe[0]; | |
} else { | |
current_frame = keyframe[0]; // start new keyframe from begining | |
} | |
// moving Bitmap inside character MovieClip | |
var character:MovieClip = stage.getChildByName("character") as MovieClip; | |
var sprite_sheet:Bitmap = character.getChildByName("sprite_sheet") as Bitmap; | |
sprite_sheet.x = -1 * current_frame * character.width; | |
} | |
// usage of flip function is because of Movieclip registration point, its a fix | |
// as the registration point of MovieClip is not placed in center, when flipping animation (for non existing directions inside spritesheet) | |
// character location changes with an unwanted value equal its width, so we have to prevent this and push it back or forward during flip | |
function flip(left:Boolean):void { | |
var character:MovieClip = stage.getChildByName("character") as MovieClip; | |
if (left) { | |
if (character.scaleX != -1) { | |
character.scaleX = -1; | |
character.x += character.width; // comment this line to see what happen without this fix | |
} | |
} else { | |
if (character.scaleX != 1) { | |
character.scaleX = 1; | |
character.x -= character.width; // comment this line to see what happen without this fix | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you so much for this, payam! Really appreciate the help, man :)