topical media & game development
actionscript-misc-TileMapRenderer.ax
actionscript-misc-TileMapRenderer.ax
[swf]
flex
package {
import flash.display.*;
import flash.utils.ByteArray;
import flash.geom.*;
// A utility class for generating a BitmapData object that represents
// an arbitrary a rectangular region of a tile map based on
// a given tile set. Note that this implementation is fairly brute force.
// Each time the map is rendered, the entire display region is generated
// anew. Greater performance could be achieved by using buffers and
// BitmapData.scroll() to avoid unnecessary redrawing of recurring pixels.
// For details on one such possible implementation, see:
// http://gpwiki.org/index.php/Framing_Buffer_Tile_Scrolling_Algorithm
public class @ax-actionscript-misc-TileMapRenderer implements actionscript_misc_ITileMapRenderer {
private var map:actionscript_misc_TileMap; // The map to render
private var tiles:actionscript_misc_TileSet; // The tiles used to render the map
// Constructor
public function @ax-actionscript-misc-TileMapRenderer (map:actionscript_misc_TileMap = null,
tiles:actionscript_misc_TileSet = null) {
setMap(map);
setTiles(tiles);
}
// Sets the tile map to be displayed
public function setMap (newMap:actionscript_misc_TileMap):void {
map = newMap;
}
// Sets the tiles from which the tile map will be rendered
public function setTiles (newTiles:actionscript_misc_TileSet):void {
tiles = newTiles;
}
// Renders an arbitrary section of the map to a BitmapData object,
// which is then returned. The section to render is specified in pixels.
public function getRenderedRegionByPixels (rect:Rectangle):BitmapData {
var tileSize:int = tiles.getTileSize();
var mapNumCols:int = map.getNumCols();
var mapNumRows:int = map.getNumRows();
// If necessary, adjust width and height to ensure that the display
// region displays at least one tile.
if (rect.width < tileSize) {
rect.width = tileSize;
}
if (rect.height < tileSize) {
rect.height = tileSize;
}
// If necessary, adjust width and height to ensure that the display
// region does not exceed the size of the world map.
if (rect.width > mapNumCols*tileSize) {
rect.width = mapNumCols*tileSize;
}
if (rect.height > mapNumRows*tileSize) {
rect.height = mapNumRows*tileSize;
}
// If necessary, adjust x and y to ensure that the display region
// is always within the bounds of the world map.
if (rect.x < 0) {
rect.x = 0;
}
if (rect.y < 0) {
rect.y = 0;
}
if (rect.x+rect.width > mapNumCols*tileSize) {
rect.x = mapNumCols*tileSize - rect.width;
}
if (rect.y+rect.height > mapNumRows*tileSize) {
rect.y = mapNumRows*tileSize - rect.height;
}
// Done cleaning the data; now comes the rendering.
// In order to return a rectangle of pixels representing a
// region of the tile map, we must first render the tile map to a
// temporary buffer. Then, we copy the pixels we want from that
// buffer to a final BitmapData object. In theory, we could just
// render the entire world map once to a large reusable buffer,
// and pick the region to render from that, but a world buffer would
// be limited to 2880x2880, and could consume unacceptable amounts
// of memory. Instead, we buffer only a portion of the map, based
// on the size of the requested pixel rectangle.
// Identify which rows and columns on the map contain the specified
// pixel rectangle
var xOffset:int = rect.x\%tileSize;
var yOffset:int = rect.y\%tileSize;
var startCol:int = Math.floor(rect.x/tileSize);
var startRow:int = Math.floor(rect.y/tileSize);
var numColsToRender:int = Math.ceil((rect.width+xOffset)/tileSize);
var numRowsToRender:int = Math.ceil((rect.height+yOffset)/tileSize);
// Render the rows and columns containing the specified pixel
// rectangle to a temporary buffer.
var tempBuffer:BitmapData = getRenderedRegionByCoords(startCol,
startRow, numColsToRender, numRowsToRender);
// Copy the specified pixel rectangle from the temporary buffer to
// the final BitmapData object
var renderedRegion:BitmapData = new BitmapData(rect.width,
rect.height);
renderedRegion.copyPixels(tempBuffer,
new Rectangle(xOffset, yOffset,
rect.width, rect.height),
new Point(0,0));
// Return the BitmapData object containing the requested
// rectangular pixel region
return renderedRegion;
}
// Renders an arbitrary section of the map to a BitmapData object,
// which is then returned. The section to render is specified in map
// coordinates (i.e., rows and columns).
public function getRenderedRegionByCoords (startCol:int,
startRow:int,
numCols:int,
numRows:int):BitmapData {
// Create a BitmapData object to hold the rendered region
var renderedRegion:BitmapData = new BitmapData(
numCols * tiles.getTileSize(),
numRows * tiles.getTileSize());
// Determine the bottom-most row and right-most column to render.
var endCol:int = startCol + numCols;
var endRow:int = startRow + numRows;
// Render the specified tiles
var thisTile:ByteArray;
var tileSize:int = tiles.getTileSize();
for (var i:int = startRow; i < endRow; i++) {
for (var j:int = startCol; j < endCol; j++) {
// BitmapData.setPixels() converts a tile stored as a ByteArray to
// a rectangular area within the BitmapData object (in this case,
// renderedRegion).
thisTile = tiles.getTile(map.getTile(j, i));
thisTile.position = 0;
renderedRegion.setPixels(new Rectangle((j-startCol)*tileSize,
(i-startRow)*tileSize,
tileSize, tileSize),
thisTile);
}
}
// Return the BitmapData object containing the requested
// rectangular region
return renderedRegion;
}
}
}
(C) Æliens
27/08/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.