Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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;
}
}
}
@lovewqcm520

This comment has been minimized.

Copy link

lovewqcm520 commented Sep 14, 2012

Excellent job!
Help me a lot.

Thanks.

@injunech

This comment has been minimized.

Copy link

injunech commented Feb 14, 2013

Hoe can I use?

plz make a example source code

@jjppof

This comment has been minimized.

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
You can’t perform that action at this time.