Skip to content

Instantly share code, notes, and snippets.

@shaunus84
Created July 31, 2012 12:15
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save shaunus84/3216598 to your computer and use it in GitHub Desktop.
Save shaunus84/3216598 to your computer and use it in GitHub Desktop.
TMX Loader Starling Extension
/*
* Copyright (C) 2012 Jean-Philippe Auclair
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
* Base64 library for ActionScript 3.0.
* By: Jean-Philippe Auclair : http://jpauclair.net
* Based on article: http://jpauclair.net/2010/01/09/base64-optimized-as3-lib/
* Benchmark:
* This version: encode: 260ms decode: 255ms
* Blog version: encode: 322ms decode: 694ms
* as3Crypto encode: 6728ms decode: 4098ms
*
* Encode: com.sociodox.utils.Base64 is 25.8x faster than as3Crypto Base64
* Decode: com.sociodox.utils.Base64 is 16x faster than as3Crypto Base64
*
* Optimize & Profile any Flash content with TheMiner ( http://www.sociodox.com/theminer )
*/
package
{
import flash.utils.ByteArray;
public class Base64
{
private static const _encodeChars:Vector.<int> = InitEncoreChar();
private static const _decodeChars:Vector.<int> = InitDecodeChar();
public static function encode(data:ByteArray):String
{
var out:ByteArray = new ByteArray();
//Presetting the length keep the memory smaller and optimize speed since there is no "grow" needed
out.length = (2 + data.length - ((data.length + 2) % 3)) * 4 / 3; //Preset length //1.6 to 1.5 ms
var i:int = 0;
var r:int = data.length % 3;
var len:int = data.length - r;
var c:uint; //read (3) character AND write (4) characters
var outPos:int = 0;
while (i < len)
{
//Read 3 Characters (8bit * 3 = 24 bits)
c = data[int(i++)] << 16 | data[int(i++)] << 8 | data[int(i++)];
out[int(outPos++)] = _encodeChars[int(c >>> 18)];
out[int(outPos++)] = _encodeChars[int(c >>> 12 & 0x3f)];
out[int(outPos++)] = _encodeChars[int(c >>> 6 & 0x3f)];
out[int(outPos++)] = _encodeChars[int(c & 0x3f)];
}
if (r == 1) //Need two "=" padding
{
//Read one char, write two chars, write padding
c = data[int(i)];
out[int(outPos++)] = _encodeChars[int(c >>> 2)];
out[int(outPos++)] = _encodeChars[int((c & 0x03) << 4)];
out[int(outPos++)] = 61;
out[int(outPos++)] = 61;
}
else if (r == 2) //Need one "=" padding
{
c = data[int(i++)] << 8 | data[int(i)];
out[int(outPos++)] = _encodeChars[int(c >>> 10)];
out[int(outPos++)] = _encodeChars[int(c >>> 4 & 0x3f)];
out[int(outPos++)] = _encodeChars[int((c & 0x0f) << 2)];
out[int(outPos++)] = 61;
}
return out.readUTFBytes(out.length);
}
public static function decode(str:String):ByteArray
{
var c1:int;
var c2:int;
var c3:int;
var c4:int;
var i:int = 0;
var len:int = str.length;
var byteString:ByteArray = new ByteArray();
byteString.writeUTFBytes(str);
var outPos:int = 0;
while (i < len)
{
//c1
c1 = _decodeChars[int(byteString[i++])];
if (c1 == -1)
break;
//c2
c2 = _decodeChars[int(byteString[i++])];
if (c2 == -1)
break;
byteString[int(outPos++)] = (c1 << 2) | ((c2 & 0x30) >> 4);
//c3
c3 = byteString[int(i++)];
if (c3 == 61)
{
byteString.length = outPos
return byteString;
}
c3 = _decodeChars[int(c3)];
if (c3 == -1)
break;
byteString[int(outPos++)] = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
//c4
c4 = byteString[int(i++)];
if (c4 == 61)
{
byteString.length = outPos
return byteString;
}
c4 = _decodeChars[int(c4)];
if (c4 == -1)
break;
byteString[int(outPos++)] = ((c3 & 0x03) << 6) | c4;
}
byteString.length = outPos
return byteString;
}
public static function InitEncoreChar():Vector.<int>
{
var encodeChars:Vector.<int> = new Vector.<int>(64, true);
// We could push the number directly
// but I think it's nice to see the characters (with no overhead on encode/decode)
var chars:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (var i:int = 0; i < 64; i++)
{
encodeChars[i] = chars.charCodeAt(i);
}
return encodeChars;
}
public static function InitDecodeChar():Vector.<int>
{
var decodeChars:Vector.<int> = new <int>[
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];
return decodeChars;
}
}
}
package
{
import starling.display.Sprite;
/**
* @author shaun.mitchell
*/
public class TMXLayer extends Sprite
{
private var _layerData:Array = new Array();
private var _layerHolder:Sprite = new Sprite;
public function TMXLayer(data:Array):void
{
_layerData = data;
}
public function getData():Array
{
return _layerData;
}
public function getHolder():Sprite
{
return _layerHolder;
}
public function drawLayer():void
{
addChild(_layerHolder);
}
}
}
package
{
import starling.display.Image;
import starling.display.Sprite;
import starling.events.Event;
import flash.display.Bitmap;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.ByteArray;
/**
* @author shaun.mitchell
*/
public class TMXTileMap extends Sprite
{
// The TMX file to load
private var _fileName:String;
private var _loader:URLLoader;
private var _mapLoaded:Boolean;
// XML of TMX file
private var _TMX:XML;
// Layers and tilesheet holders
private var _layers:Vector.<TMXLayer>;
private var _tilesheets:Vector.<TMXTileSheet>;
// variables pertaining to map description
private var _numLayers:uint;
private var _numTilesets:uint;
private var _tilelistCount:uint;
private var _mapWidth:uint;
private var _tileHeight:uint;
private var _tileWidth:uint;
// used to get the correct tile from various tilesheets
private var _gidLookup:Vector.<uint>;
private var _embedTilesets:Vector.<Bitmap>;
public function TMXTileMap():void
{
_mapLoaded = false;
_fileName = "";
_loader = new URLLoader();
_numLayers = 0;
_numTilesets = 0;
_tilelistCount = 0;
_mapWidth = 0;
_tileHeight = 0;
_tileWidth = 0;
_layers = new Vector.<TMXLayer>();
_tilesheets = new Vector.<TMXTileSheet>();
_gidLookup = new Vector.<uint>();
}
public function load(file:String):void
{
_fileName = file;
trace(_fileName);
_loader.addEventListener(flash.events.Event.COMPLETE, loadTilesets);
_loader.load(new URLRequest(_fileName));
}
public function loadFromEmbed(tmx:XML, tilesets:Vector.<Bitmap>):void
{
_TMX = tmx;
_embedTilesets = tilesets;
loadEmbedTilesets();
}
// Getters ------------------------------------------
public function layers():Vector.<TMXLayer>
{
return _layers;
}
public function tilesheets():Vector.<TMXTileSheet>
{
return _tilesheets;
}
public function numLayers():uint
{
return _numLayers;
}
public function numTilesets():uint
{
return _numTilesets;
}
public function mapWidth():uint
{
return _mapWidth;
}
public function tileHeight():uint
{
return _tileHeight;
}
public function tileWidth():uint
{
return _tileWidth;
}
// End getters --------------------------------------
// get the number of tilsets from the TMX XML
private function getNumTilesets():uint
{
if (_mapLoaded)
{
var count:uint = 0;
for (var i:int = 0; i < _TMX.children().length(); i++)
{
if (_TMX.tileset[i] != null)
{
count++;
}
}
trace(count);
return count;
}
return 0;
}
// get the number of layers from the TMX XML
private function getNumLayers():uint
{
if (_mapLoaded)
{
var count:uint = 0;
for (var i:int = 0; i < _TMX.children().length(); i++)
{
if (_TMX.layer[i] != null)
{
count++;
}
}
trace(count);
return count;
}
return 0;
}
private function loadTilesets(event:flash.events.Event):void
{
trace("loading tilesets from file");
_mapLoaded = true;
_TMX = new XML(_loader.data);
if (_TMX)
{
_mapWidth = _TMX.@width;
_tileHeight = _TMX.@tileheight;
_tileWidth = _TMX.@tilewidth;
trace("map width" + _mapWidth);
_numLayers = getNumLayers();
_numTilesets = getNumTilesets();
// _TMX.properties.property[1].@value;
var tileSheet:TMXTileSheet = new TMXTileSheet();
tileSheet.loadTileSheet(_TMX.tileset[_tilelistCount].@name, _TMX.tileset[_tilelistCount].image.@source, _TMX.tileset[_tilelistCount].@tilewidth, _TMX.tileset[_tilelistCount].@tileheight, _TMX.tileset[_tilelistCount].@firstgid - 1);
tileSheet.addEventListener(starling.events.Event.COMPLETE, loadRemainingTilesets);
_tilesheets.push(tileSheet);
_gidLookup.push(_TMX.tileset[_tilelistCount].@firstgid);
}
}
private function loadEmbedTilesets():void
{
trace("loading embedded tilesets");
_mapLoaded = true;
if (_TMX)
{
_mapWidth = _TMX.@width;
_tileHeight = _TMX.@tileheight;
_tileWidth = _TMX.@tilewidth;
trace("map width" + _mapWidth);
_numLayers = getNumLayers();
_numTilesets = getNumTilesets();
trace(_numTilesets);
// _TMX.properties.property[1].@value;
for (var i:int = 0; i < _numTilesets; i++)
{
var tileSheet:TMXTileSheet = new TMXTileSheet();
trace(_TMX.tileset[i].@name, _embedTilesets[i], _TMX.tileset[i].@tilewidth, _TMX.tileset[i].@tileheight, _TMX.tileset[i].@firstgid - 1);
tileSheet.loadEmbedTileSheet(_TMX.tileset[i].@name, _embedTilesets[i], _TMX.tileset[i].@tilewidth, _TMX.tileset[i].@tileheight, _TMX.tileset[i].@firstgid - 1);
_tilesheets.push(tileSheet);
_gidLookup.push(_TMX.tileset[i].@firstgid);
}
loadMapData();
}
}
private function loadRemainingTilesets(event:starling.events.Event):void
{
event.target.removeEventListener(starling.events.Event.COMPLETE, loadRemainingTilesets);
_tilelistCount++;
if (_tilelistCount >= _numTilesets)
{
trace("done loading tilelists");
loadMapData();
}
else
{
trace(_TMX.tileset[_tilelistCount].@name);
var tileSheet:TMXTileSheet = new TMXTileSheet();
tileSheet.loadTileSheet(_TMX.tileset[_tilelistCount].@name, _TMX.tileset[_tilelistCount].image.@source, _TMX.tileset[_tilelistCount].@tilewidth, _TMX.tileset[_tilelistCount].@tileheight, _TMX.tileset[_tilelistCount].@firstgid - 1);
tileSheet.addEventListener(starling.events.Event.COMPLETE, loadRemainingTilesets);
_gidLookup.push(_TMX.tileset[_tilelistCount].@firstgid);
_tilesheets.push(tileSheet);
}
}
private function loadMapData():void
{
if (_mapLoaded)
{
for (var i:int = 0; i < _numLayers; i++)
{
trace("loading map data");
var ba:ByteArray = Base64.decode(_TMX.layer[i].data);
ba.uncompress();
var data:Array = new Array();
for (var j:int = 0; j < ba.length; j += 4)
{
// Get the grid ID
var a:int = ba[j];
var b:int = ba[j + 1];
var c:int = ba[j + 2];
var d:int = ba[j + 3];
var gid:int = a | b << 8 | c << 16 | d << 24;
data.push(gid);
}
var tmxLayer:TMXLayer = new TMXLayer(data);
_layers.push(tmxLayer);
}
drawLayers();
}
}
// draw the layers into a holder contained in a TMXLayer object
private function drawLayers():void
{
trace("drawing layers");
for (var i:int = 0; i < _numLayers; i++)
{
trace("drawing layers");
var row:int = 0;
var col:int = 0;
for (var j:int = 0; j < _layers[i].getData().length; j++)
{
if (col > (_mapWidth - 1) * _tileWidth)
{
col = 0;
row += _tileHeight;
}
if (_layers[i].getData()[j] != 0)
{
var img:Image = new Image(_tilesheets[findTileSheet(_layers[i].getData()[j])].textureAtlas.getTexture(String(_layers[i].getData()[j])));
img.x = col;
img.y = row;
_layers[i].getHolder().addChild(img);
}
col += _tileWidth;
}
}
// notify that the load is complete
dispatchEvent(new starling.events.Event(starling.events.Event.COMPLETE));
}
private function findTileSheet(id:uint):int
{
var value:int = 0;
var theOne:int;
for (var i:int = 0; i < _tilesheets.length; i++)
{
if (_tilesheets[i].textureAtlas.getTexture(String(id)) != null)
{
theOne = i;
}
else
{
value = i;
}
}
return theOne;
}
}
}
package
{
import starling.display.Sprite;
import starling.events.Event;
import starling.textures.Texture;
import starling.textures.TextureAtlas;
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
/**
* @author shaun.mitchell
*/
public class TMXTileSheet extends Sprite
{
// the name and file paths
private var _name:String;
private var _sheetFilename:String;
// texture, atlas and loader
private var _sheet:Bitmap;
private var _textureAtlas:TextureAtlas;
private var _imageLoader:Loader = new Loader();
private var _startID:uint;
private var _tileHeight:uint;
private var _tileWidth:uint;
private var _embedded:Boolean;
public function TMXTileSheet():void
{
}
public function loadTileSheet(name:String, sheetFile:String, tileWidth:uint, tileHeight:uint, startID:uint):void
{
_embedded = false;
_name = name;
_sheetFilename = sheetFile;
_startID = startID;
_tileHeight = tileHeight;
_tileWidth = tileWidth;
trace("creating TMX tilesheet");
_imageLoader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, loadSheet);
_imageLoader.load(new URLRequest(_sheetFilename));
}
public function loadEmbedTileSheet(name:String, img:Bitmap, tileWidth:uint, tileHeight:uint, startID:uint):void
{
trace("creating TMX tilesheet");
_embedded = true;
_name = name;
_startID = startID;
_sheet = img;
_tileHeight = tileHeight;
_tileWidth = tileWidth;
loadAtlas();
}
/*
Load the image file needed for this tilesheet
*/
private function loadSheet(event:flash.events.Event):void
{
var sprite:DisplayObject = _imageLoader.content;
_sheet = Bitmap(sprite);
loadAtlas();
}
/*
dynamically create a texture atlas to look up tiles
*/
private function loadAtlas():void
{
trace("loading atlas");
var numRows:uint = _sheet.height / _tileHeight;
var numCols:uint = _sheet.width / _tileWidth;
var id:int = _startID;
var xml:XML = <Atlas></Atlas>;
xml.appendChild(<TextureAtlas imagePath={_sheetFilename}></TextureAtlas>);
for (var i:int = 0; i < numRows; i++)
{
for (var j:int = 0; j < numCols; j++)
{
id++;
xml.child("TextureAtlas").appendChild(<SubTexture name={id} x = {j * _tileWidth} y={i * _tileHeight } width={_tileWidth} height={_tileHeight}/>);
}
}
var newxml:XML = XML(xml.TextureAtlas);
trace(newxml);
_textureAtlas = new TextureAtlas(Texture.fromBitmap(_sheet), newxml);
trace("done with atlas, dispatching");
dispatchEvent(new starling.events.Event(starling.events.Event.COMPLETE));
}
public function get sheet():Bitmap
{
return _sheet;
}
public function get textureAtlas():TextureAtlas
{
return _textureAtlas;
}
}
}
@injunech
Copy link

Hoe can I use?

plz make a example source code

@jjppof
Copy link

jjppof commented Jan 21, 2015

Is there a way to get properties from each layer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment