Here’s my ProgressiveTilemap class.
It’s a copy of the FlashPunk Tilemap class but with some optimisations. Instead of using a buffer the size of the map, it uses one the size of the screen and redraws the map when the camera is scrolled, thus reducing memory consumption by a lot for large maps and not really affecting process time.
##Version 0.5: This is an unfinished version. The graphic’s x and y position aren’t implemented. Scroll X and Y isn’t implemented. Tilemap’s usePositions isn’t implemented. The majority of the Tilemap API isn’t implemented. Still, it works for the basic tilemaps. Not documented.
package com.abeltoy.utils
{
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.getTimer;
import net.flashpunk.FP;
import net.flashpunk.Graphic;
/**
* ...
* @author Abel Toy
*/
public class ProgressiveTilemap extends Graphic
{
private var _posX:int = 0;
private var _posY:int = 0;
private var _lastCamColumn:int = 0;
private var _lastCamRow:int = 0;
private var _width:uint;
private var _height:uint;
private var _columns:uint;
private var _rows:uint;
private var _map:Vector.<uint>;
private var _redraw:Boolean = false;
private var _bitmap:BitmapData;
private var _tile:Rectangle;
private var _set:BitmapData;
private var _setColumns:uint;
private var _setRows:uint;
private var _setCount:uint;
private var _screenTileW:int;
private var _screenTileH:int;
public function ProgressiveTilemap(tileset:*, width:uint, height:uint, tileWidth:uint, tileHeight:uint)
{
super();
_width = width;
_height = height;
_columns = Math.ceil(_width / tileWidth);
_rows = Math.ceil(_height / tileHeight);
_map = new Vector.<uint>(_columns * _rows, true);
_screenTileW = Math.ceil(FP.width / tileWidth);
_screenTileH = Math.ceil(FP.height / tileHeight);
_bitmap = new BitmapData(_screenTileW * tileWidth + tileWidth * 2, _screenTileH * tileHeight + tileHeight * 2, true, 0);
_tile = new Rectangle(0, 0, tileWidth, tileHeight);
_point = new Point();
if (tileset is Class)
_set = FP.getBitmap(tileset);
else if (tileset is BitmapData)
_set = tileset;
if (!_set)
throw new Error("Invalid tileset graphic provided.");
_setColumns = Math.ceil(_set.width / tileWidth);
_setRows = Math.ceil(_set.height / tileHeight);
_setCount = _setColumns * _setRows;
}
public function setTile(column:uint, row:uint, index:uint = 0):void
{
column %= _columns;
row %= _rows;
index %= _setCount;
_map[(row * _columns) + column] = index;
_redraw = true;
//TODO: only redraw when the set tile is on camera.
}
public function getTile(column:uint, row:uint):uint
{
return _map[((row % _rows) * _columns) + (column % _columns)];
}
private function _draw(offsetX:int, offsetY:int):void
{
var x:int = offsetX - 1;
var y:int = offsetY - 1;
var w:int = _screenTileW + offsetX + 1;
var h:int = _screenTileH + offsetY + 1;
if (x < 0) x = 0;
if (y < 0) y = 0;
if (w > _columns) w = _columns;
if (h > _rows) h = _rows;
while (y < h)
{
while (x < w)
{
var index:int = _map[(y * _columns) + x];
_drawTile(x - offsetX, y - offsetY, index);
x++;
}
x = offsetX;
y++;
}
}
private function _drawTile(x:uint, y:uint, index:uint):void
{
_tile.x = (index % _setColumns) * _tile.width;
_tile.y = uint(index / _setColumns) * _tile.height;
_point.x = x * _tile.width;
_point.y = y * _tile.height;
_bitmap.copyPixels(_set, _tile, _point);
}
override public function render(target:BitmapData, point:Point, camera:Point):void
{
var camColumn:int = Math.floor(camera.x / _tile.width);
var camRow:int = Math.floor(camera.y / _tile.height);
if (camColumn != _lastCamColumn || camRow != _lastCamRow) _redraw = true;
if (_redraw)
{
_draw(camColumn, camRow);
_redraw = false;
_lastCamColumn = camColumn;
_lastCamRow = camRow;
}
_posX = - camera.x % _tile.width;
_posY = - camera.y % _tile.height;
_point.x = _posX;
_point.y = _posY;
//TODO: implement x and y position
//TODO: implement scrollX and scrollY
target.copyPixels(_bitmap, _bitmap.rect, _point, null, null, true);
}
//TODO: implement Tilemap additional functions
//TODO: implement usePositions
public function loadFromString(str:String, columnSep:String = ",", rowSep:String = "\n"):void
{
var row:Array = str.split(rowSep),
rows:int = row.length,
col:Array, cols:int, x:int, y:int;
for (y = 0; y < rows; y ++)
{
if (row[y] == '') continue;
col = row[y].split(columnSep),
cols = col.length;
for (x = 0; x < cols; x ++)
{
if (col[x] == '' || col[x] == "-1") continue;
setTile(x, y, uint(col[x]));
}
}
}
public function get tileWidth():uint
{
return _tile.width;
}
public function get tileHeight():uint
{
return _tile.height;
}
public function get tileCount():uint
{
return _setCount;
}
public function get columns():uint
{
return _columns;
}
public function get rows():uint
{
return _rows;
}
}
}