PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/workspace_actionscript/Hello World/src/net/flashpunk/graphics/Tilemap.as

https://code.google.com/
ActionScript | 517 lines | 342 code | 48 blank | 127 comment | 47 complexity | bdb77682af7e5d0aed062e246fe36531 MD5 | raw file
  1. package net.flashpunk.graphics
  2. {
  3. import flash.display.BitmapData;
  4. import flash.geom.Point;
  5. import flash.geom.Rectangle;
  6. import net.flashpunk.FP;
  7. import net.flashpunk.Graphic;
  8. import net.flashpunk.masks.Grid;
  9. import net.flashpunk.utils.Draw;
  10. /**
  11. * A canvas to which Tiles can be drawn for fast multiple tile rendering.
  12. */
  13. public class Tilemap extends Canvas
  14. {
  15. /**
  16. * If x/y positions should be used instead of columns/rows.
  17. */
  18. public var usePositions:Boolean;
  19. /**
  20. * Constructor.
  21. * @param tileset The source tileset image.
  22. * @param width Width of the tilemap, in pixels.
  23. * @param height Height of the tilemap, in pixels.
  24. * @param tileWidth Tile width.
  25. * @param tileHeight Tile height.
  26. */
  27. public function Tilemap(tileset:*, width:uint, height:uint, tileWidth:uint, tileHeight:uint)
  28. {
  29. // set some tilemap information
  30. _width = width;
  31. _height = height;
  32. _columns = Math.ceil(_width / tileWidth);
  33. _rows = Math.ceil(_height / tileHeight);
  34. _map = new BitmapData(_columns, _rows, false, 0);
  35. _temp = _map.clone();
  36. _tile = new Rectangle(0, 0, tileWidth, tileHeight);
  37. // create the canvas
  38. _maxWidth -= _maxWidth % tileWidth;
  39. _maxHeight -= _maxHeight % tileHeight;
  40. super(_width, _height);
  41. // load the tileset graphic
  42. if (tileset is Class) _set = FP.getBitmap(tileset);
  43. else if (tileset is BitmapData) _set = tileset;
  44. if (!_set) throw new Error("Invalid tileset graphic provided.");
  45. _setColumns = Math.ceil(_set.width / tileWidth);
  46. _setRows = Math.ceil(_set.height / tileHeight);
  47. _setCount = _setColumns * _setRows;
  48. }
  49. /**
  50. * Sets the index of the tile at the position.
  51. * @param column Tile column.
  52. * @param row Tile row.
  53. * @param index Tile index.
  54. */
  55. public function setTile(column:uint, row:uint, index:uint = 0):void
  56. {
  57. if (usePositions)
  58. {
  59. column /= _tile.width;
  60. row /= _tile.height;
  61. }
  62. index %= _setCount;
  63. column %= _columns;
  64. row %= _rows;
  65. _tile.x = (index % _setColumns) * _tile.width;
  66. _tile.y = uint(index / _setColumns) * _tile.height;
  67. _point.x = column * _tile.width;
  68. _point.y = row * _tile.height;
  69. _map.setPixel(column, row, index);
  70. copyPixels(_set, _tile, _point, null, null, false);
  71. }
  72. /**
  73. * Clears the tile at the position.
  74. * @param column Tile column.
  75. * @param row Tile row.
  76. */
  77. public function clearTile(column:uint, row:uint):void
  78. {
  79. if (usePositions)
  80. {
  81. column /= _tile.width;
  82. row /= _tile.height;
  83. }
  84. column %= _columns;
  85. row %= _rows;
  86. _tile.x = column * _tile.width;
  87. _tile.y = row * _tile.height;
  88. fill(_tile, 0, 0);
  89. }
  90. /**
  91. * Gets the tile index at the position.
  92. * @param column Tile column.
  93. * @param row Tile row.
  94. * @return The tile index.
  95. */
  96. public function getTile(column:uint, row:uint):uint
  97. {
  98. if (usePositions)
  99. {
  100. column /= _tile.width;
  101. row /= _tile.height;
  102. }
  103. return _map.getPixel(column % _columns, row % _rows);
  104. }
  105. /**
  106. * Sets a rectangular region of tiles to the index.
  107. * @param column First tile column.
  108. * @param row First tile row.
  109. * @param width Width in tiles.
  110. * @param height Height in tiles.
  111. * @param index Tile index.
  112. */
  113. public function setRect(column:uint, row:uint, width:uint = 1, height:uint = 1, index:uint = 0):void
  114. {
  115. if (usePositions)
  116. {
  117. column /= _tile.width;
  118. row /= _tile.height;
  119. width /= _tile.width;
  120. height /= _tile.height;
  121. }
  122. column %= _columns;
  123. row %= _rows;
  124. var c:uint = column,
  125. r:uint = column + width,
  126. b:uint = row + height,
  127. u:Boolean = usePositions;
  128. usePositions = false;
  129. while (row < b)
  130. {
  131. while (column < r)
  132. {
  133. setTile(column, row, index);
  134. column ++;
  135. }
  136. column = c;
  137. row ++;
  138. }
  139. usePositions = u;
  140. }
  141. /**
  142. * Makes a flood fill on the tilemap
  143. * @param column Column to place the flood fill
  144. * @param row Row to place the flood fill
  145. * @param index Tile index.
  146. */
  147. public function floodFill(column:uint, row:uint, index:uint = 0):void
  148. {
  149. if(usePositions)
  150. {
  151. column /= _tile.width;
  152. row /= _tile.height;
  153. }
  154. column %= _columns;
  155. row %= _rows;
  156. _map.floodFill(column, row, index);
  157. updateAll();
  158. }
  159. /**
  160. * Draws a line of tiles
  161. *
  162. * @param x1 The x coordinate to start
  163. * @param y1 The y coordinate to start
  164. * @param x2 The x coordinate to end
  165. * @param y2 The y coordinate to end
  166. * @param id The tiles id to draw
  167. *
  168. */
  169. public function line(x1:int, y1:int, x2:int, y2:int, id:int):void
  170. {
  171. if(usePositions)
  172. {
  173. x1 /= _tile.width;
  174. y1 /= _tile.height;
  175. x2 /= _tile.width;
  176. y2 /= _tile.height;
  177. }
  178. x1 %= _columns;
  179. y1 %= _rows;
  180. x2 %= _columns;
  181. y2 %= _rows;
  182. Draw.setTarget(_map);
  183. Draw.line(x1, y1, x2, y2, id, 0);
  184. updateAll();
  185. }
  186. /**
  187. * Draws an outline of a rectangle of tiles
  188. *
  189. * @param x The x coordinate of the rectangle
  190. * @param y The y coordinate of the rectangle
  191. * @param width The width of the rectangle
  192. * @param height The height of the rectangle
  193. * @param id The tiles id to draw
  194. *
  195. */
  196. public function setRectOutline(x:int, y:int, width:int, height:int, id:int):void
  197. {
  198. if(usePositions)
  199. {
  200. x /= _tile.width;
  201. y /= _tile.height;
  202. // TODO: might want to use difference between converted start/end coordinates instead?
  203. width /= _tile.width;
  204. height /= _tile.height;
  205. }
  206. x %= _columns;
  207. y %= _rows;
  208. Draw.setTarget(_map);
  209. Draw.line(x, y, x + width, y, id, 0);
  210. Draw.line(x, y + height, x + width, y + height, id, 0);
  211. Draw.line(x, y, x, y + height, id, 0);
  212. Draw.line(x + width, y, x + width, y + height, id, 0);
  213. updateAll();
  214. }
  215. /**
  216. * Updates the graphical cache for the whole tilemap.
  217. */
  218. public function updateAll():void
  219. {
  220. _rect.x = 0;
  221. _rect.y = 0;
  222. _rect.width = _columns;
  223. _rect.height = _rows;
  224. updateRect(_rect, false);
  225. }
  226. /**
  227. * Clears the rectangular region of tiles.
  228. * @param column First tile column.
  229. * @param row First tile row.
  230. * @param width Width in tiles.
  231. * @param height Height in tiles.
  232. */
  233. public function clearRect(column:uint, row:uint, width:uint = 1, height:uint = 1):void
  234. {
  235. if (usePositions)
  236. {
  237. column /= _tile.width;
  238. row /= _tile.height;
  239. width /= _tile.width;
  240. height /= _tile.height;
  241. }
  242. column %= _columns;
  243. row %= _rows;
  244. var c:uint = column,
  245. r:uint = column + width,
  246. b:uint = row + height,
  247. u:Boolean = usePositions;
  248. usePositions = false;
  249. while (row < b)
  250. {
  251. while (column < r)
  252. {
  253. clearTile(column, row);
  254. column ++;
  255. }
  256. column = c;
  257. row ++;
  258. }
  259. usePositions = u;
  260. }
  261. /**
  262. * Loads the Tilemap tile index data from a string.
  263. * @param str The string data, which is a set of tile values separated by the columnSep and rowSep strings.
  264. * @param columnSep The string that separates each tile value on a row, default is ",".
  265. * @param rowSep The string that separates each row of tiles, default is "\n".
  266. */
  267. public function loadFromString(str:String, columnSep:String = ",", rowSep:String = "\n"):void
  268. {
  269. var u:Boolean = usePositions;
  270. usePositions = false;
  271. var row:Array = str.split(rowSep),
  272. rows:int = row.length,
  273. col:Array, cols:int, x:int, y:int;
  274. for (y = 0; y < rows; y ++)
  275. {
  276. if (row[y] == '') continue;
  277. col = row[y].split(columnSep),
  278. cols = col.length;
  279. for (x = 0; x < cols; x ++)
  280. {
  281. if (col[x] == '') continue;
  282. setTile(x, y, uint(col[x]));
  283. }
  284. }
  285. usePositions = u;
  286. }
  287. /**
  288. * Saves the Tilemap tile index data to a string.
  289. * @param columnSep The string that separates each tile value on a row, default is ",".
  290. * @param rowSep The string that separates each row of tiles, default is "\n".
  291. */
  292. public function saveToString(columnSep:String = ",", rowSep:String = "\n"): String
  293. {
  294. var s:String = '',
  295. x:int, y:int;
  296. for (y = 0; y < _rows; y ++)
  297. {
  298. for (x = 0; x < _columns; x ++)
  299. {
  300. s += String(_map.getPixel(x, y));
  301. if (x != _columns - 1) s += columnSep;
  302. }
  303. if (y != _rows - 1) s += rowSep;
  304. }
  305. return s;
  306. }
  307. /**
  308. * Gets the 1D index of a tile from a 2D index (its column and row in the tileset image).
  309. * @param tilesColumn Tileset column.
  310. * @param tilesRow Tileset row.
  311. * @return Index of the tile.
  312. */
  313. public function getIndex(tilesColumn:uint, tilesRow:uint):uint
  314. {
  315. if (usePositions) {
  316. tilesColumn /= _tile.width;
  317. tilesRow /= _tile.height;
  318. }
  319. return (tilesRow % _setRows) * _setColumns + (tilesColumn % _setColumns);
  320. }
  321. /**
  322. * Shifts all the tiles in the tilemap.
  323. * @param columns Horizontal shift.
  324. * @param rows Vertical shift.
  325. * @param wrap If tiles shifted off the canvas should wrap around to the other side.
  326. */
  327. public function shiftTiles(columns:int, rows:int, wrap:Boolean = false):void
  328. {
  329. if (usePositions)
  330. {
  331. columns /= _tile.width;
  332. rows /= _tile.height;
  333. }
  334. if (!wrap) _temp.fillRect(_temp.rect, 0);
  335. if (columns != 0)
  336. {
  337. shift(columns * _tile.width, 0);
  338. if (wrap) _temp.copyPixels(_map, _map.rect, FP.zero);
  339. _map.scroll(columns, 0);
  340. _point.y = 0;
  341. _point.x = columns > 0 ? columns - _columns : columns + _columns;
  342. _map.copyPixels(_temp, _temp.rect, _point);
  343. _rect.x = columns > 0 ? 0 : _columns + columns;
  344. _rect.y = 0;
  345. _rect.width = Math.abs(columns);
  346. _rect.height = _rows;
  347. updateRect(_rect, !wrap);
  348. }
  349. if (rows != 0)
  350. {
  351. shift(0, rows * _tile.height);
  352. if (wrap) _temp.copyPixels(_map, _map.rect, FP.zero);
  353. _map.scroll(0, rows);
  354. _point.x = 0;
  355. _point.y = rows > 0 ? rows - _rows : rows + _rows;
  356. _map.copyPixels(_temp, _temp.rect, _point);
  357. _rect.x = 0;
  358. _rect.y = rows > 0 ? 0 : _rows + rows;
  359. _rect.width = _columns;
  360. _rect.height = Math.abs(rows);
  361. updateRect(_rect, !wrap);
  362. }
  363. }
  364. /**
  365. * Get a subregion of the tilemap and return it as a new Tilemap.
  366. */
  367. public function getSubMap (x:int, y:int, w:int, h:int):Tilemap
  368. {
  369. if (usePositions) {
  370. x /= _tile.width;
  371. y /= _tile.height;
  372. w /= _tile.width;
  373. h /= _tile.height;
  374. }
  375. var newMap:Tilemap = new Tilemap(_set, w*_tile.width, h*_tile.height, _tile.width, _tile.height);
  376. _rect.x = x;
  377. _rect.y = y;
  378. _rect.width = w;
  379. _rect.height = h;
  380. newMap._map.copyPixels(_map, _rect, FP.zero);
  381. newMap.drawGraphic(-x * _tile.width, -y * _tile.height, this);
  382. return newMap;
  383. }
  384. /** Updates the graphical cache of a region of the tilemap. */
  385. public function updateRect(rect:Rectangle, clear:Boolean):void
  386. {
  387. var x:int = rect.x,
  388. y:int = rect.y,
  389. w:int = x + rect.width,
  390. h:int = y + rect.height,
  391. u:Boolean = usePositions;
  392. usePositions = false;
  393. if (clear)
  394. {
  395. while (y < h)
  396. {
  397. while (x < w) clearTile(x ++, y);
  398. x = rect.x;
  399. y ++;
  400. }
  401. }
  402. else
  403. {
  404. while (y < h)
  405. {
  406. while (x < w) updateTile(x ++, y);
  407. x = rect.x;
  408. y ++;
  409. }
  410. }
  411. usePositions = u;
  412. }
  413. /** @private Used by shiftTiles to update a tile from the tilemap. */
  414. private function updateTile(column:uint, row:uint):void
  415. {
  416. setTile(column, row, _map.getPixel(column % _columns, row % _rows));
  417. }
  418. /**
  419. * Create a Grid object from this tilemap.
  420. * @param solidTiles Array of tile indexes that should be solid.
  421. * @return Grid
  422. */
  423. public function createGrid(solidTiles:Array, cls:Class=null):Grid
  424. {
  425. if (cls === null) cls = Grid;
  426. var grid:Grid = new cls(width, height, _tile.width, _tile.height, 0) as Grid;
  427. for (var row:uint = 0; row < _rows; ++row)
  428. {
  429. for (var col:uint = 0; col < _columns; ++col)
  430. {
  431. if (solidTiles.indexOf(_map.getPixel(col, row)) !== -1)
  432. {
  433. grid.setTile(col, row, true);
  434. }
  435. }
  436. }
  437. return grid;
  438. }
  439. /**
  440. * The tile width.
  441. */
  442. public function get tileWidth():uint { return _tile.width; }
  443. /**
  444. * The tile height.
  445. */
  446. public function get tileHeight():uint { return _tile.height; }
  447. /**
  448. * How many columns the tilemap has.
  449. */
  450. public function get columns():uint { return _columns; }
  451. /**
  452. * How many rows the tilemap has.
  453. */
  454. public function get rows():uint { return _rows; }
  455. // Tilemap information.
  456. /** @private */ private var _map:BitmapData;
  457. /** @private */ private var _temp:BitmapData;
  458. /** @private */ private var _columns:uint;
  459. /** @private */ private var _rows:uint;
  460. // Tileset information.
  461. /** @private */ private var _set:BitmapData;
  462. /** @private */ private var _setColumns:uint;
  463. /** @private */ private var _setRows:uint;
  464. /** @private */ private var _setCount:uint;
  465. /** @private */ private var _tile:Rectangle;
  466. // Global objects.
  467. /** @private */ private var _rect:Rectangle = FP.rect;
  468. }
  469. }