topical media & game development

talk show tell print

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.