1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011 Zynga Inc. 5 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ****************************************************************************/ 26 27 /** 28 * <p>cc.TMXLayer represents the TMX layer. </p> 29 * 30 * <p>It is a subclass of cc.SpriteBatchNode. By default the tiles are rendered using a cc.TextureAtlas. <br /> 31 * If you modify a tile on runtime, then, that tile will become a cc.Sprite, otherwise no cc.Sprite objects are created. <br /> 32 * The benefits of using cc.Sprite objects as tiles are: <br /> 33 * - tiles (cc.Sprite) can be rotated/scaled/moved with a nice API </p> 34 * 35 * <p>If the layer contains a property named "cc.vertexz" with an integer (in can be positive or negative), <br /> 36 * then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth. </p> 37 * 38 * <p>On the other hand, if the "cc.vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value. <br /> 39 * Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be: </p> 40 * 41 * glAlphaFunc( GL_GREATER, value ) <br /> 42 * 43 * <p>"value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer. <br /> 44 * The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a different value, like 0.5.</p> 45 * @class 46 * @extends cc.SpriteBatchNode 47 * 48 * @property {Array} tiles - Tiles for layer 49 * @property {cc.TMXTilesetInfo} tileset - Tileset for layer 50 * @property {Number} layerOrientation - Layer orientation 51 * @property {Array} properties - Properties from the layer. They can be added using tilemap editors 52 * @property {String} layerName - Name of the layer 53 * @property {Number} layerWidth - Width of the layer 54 * @property {Number} layerHeight - Height of the layer 55 * @property {Number} tileWidth - Width of a tile 56 * @property {Number} tileHeight - Height of a tile 57 */ 58 cc.TMXLayer = cc.SpriteBatchNode.extend(/** @lends cc.TMXLayer# */{ 59 tiles: null, 60 tileset: null, 61 layerOrientation: null, 62 properties: null, 63 layerName: "", 64 65 //size of the layer in tiles 66 _layerSize: null, 67 _mapTileSize: null, 68 //TMX Layer supports opacity 69 _opacity: 255, 70 _minGID: null, 71 _maxGID: null, 72 //Only used when vertexZ is used 73 _vertexZvalue: null, 74 _useAutomaticVertexZ: null, 75 _alphaFuncValue: null, 76 //used for optimization 77 _reusedTile: null, 78 _atlasIndexArray: null, 79 //used for retina display 80 _contentScaleFactor: null, 81 82 _cacheCanvas:null, 83 _cacheContext:null, 84 _cacheTexture:null, 85 // Sub caches for avoid Chrome big image draw issue 86 _subCacheCanvas:null, 87 _subCacheContext:null, 88 _subCacheCount:0, 89 _subCacheWidth:0, 90 // Maximum pixel number by cache, a little more than 3072*3072, real limit is 4096*4096 91 _maxCachePixel:10000000, 92 _className:"TMXLayer", 93 94 /** 95 * Creates a cc.TMXLayer with an tile set info, a layer info and a map info 96 * @constructor 97 * @param {cc.TMXTilesetInfo} tilesetInfo 98 * @param {cc.TMXLayerInfo} layerInfo 99 * @param {cc.TMXMapInfo} mapInfo 100 */ 101 ctor:function (tilesetInfo, layerInfo, mapInfo) { 102 cc.SpriteBatchNode.prototype.ctor.call(this); 103 this._descendants = []; 104 105 this._layerSize = cc.size(0, 0); 106 this._mapTileSize = cc.size(0, 0); 107 108 if(cc._renderType === cc._RENDER_TYPE_CANVAS){ 109 var locCanvas = cc._canvas; 110 var tmpCanvas = cc.newElement('canvas'); 111 tmpCanvas.width = locCanvas.width; 112 tmpCanvas.height = locCanvas.height; 113 this._cacheCanvas = tmpCanvas; 114 this._cacheContext = this._cacheCanvas.getContext('2d'); 115 var tempTexture = new cc.Texture2D(); 116 tempTexture.initWithElement(tmpCanvas); 117 tempTexture.handleLoadedTexture(); 118 this._cacheTexture = tempTexture; 119 this.width = locCanvas.width; 120 this.height = locCanvas.height; 121 // This class uses cache, so its default cachedParent should be himself 122 this._cachedParent = this; 123 } 124 if(mapInfo !== undefined) 125 this.initWithTilesetInfo(tilesetInfo, layerInfo, mapInfo); 126 }, 127 128 /** 129 * Sets the untransformed size of the TMXLayer. 130 * @override 131 * @param {cc.Size|Number} size The untransformed size of the TMXLayer or The untransformed size's width of the TMXLayer. 132 * @param {Number} [height] The untransformed size's height of the TMXLayer. 133 */ 134 setContentSize:function (size, height) { 135 var locContentSize = this._contentSize; 136 cc.Node.prototype.setContentSize.call(this, size, height); 137 138 if(cc._renderType === cc._RENDER_TYPE_CANVAS){ 139 var locCanvas = this._cacheCanvas; 140 var scaleFactor = cc.contentScaleFactor(); 141 locCanvas.width = 0 | (locContentSize.width * 1.5 * scaleFactor); 142 locCanvas.height = 0 | (locContentSize.height * 1.5 * scaleFactor); 143 144 this._cacheContext.translate(0, locCanvas.height); 145 var locTexContentSize = this._cacheTexture._contentSize; 146 locTexContentSize.width = locCanvas.width; 147 locTexContentSize.height = locCanvas.height; 148 149 // Init sub caches if needed 150 var totalPixel = locCanvas.width * locCanvas.height; 151 if(totalPixel > this._maxCachePixel) { 152 if(!this._subCacheCanvas) this._subCacheCanvas = []; 153 if(!this._subCacheContext) this._subCacheContext = []; 154 155 this._subCacheCount = Math.ceil( totalPixel / this._maxCachePixel ); 156 var locSubCacheCanvas = this._subCacheCanvas, i; 157 for(i = 0; i < this._subCacheCount; i++) { 158 if(!locSubCacheCanvas[i]) { 159 locSubCacheCanvas[i] = document.createElement('canvas'); 160 this._subCacheContext[i] = locSubCacheCanvas[i].getContext('2d'); 161 } 162 var tmpCanvas = locSubCacheCanvas[i]; 163 tmpCanvas.width = this._subCacheWidth = Math.round( locCanvas.width / this._subCacheCount ); 164 tmpCanvas.height = locCanvas.height; 165 } 166 // Clear wasted cache to release memory 167 for(i = this._subCacheCount; i < locSubCacheCanvas.length; i++) { 168 tmpCanvas.width = 0; 169 tmpCanvas.height = 0; 170 } 171 } 172 // Otherwise use count as a flag to disable sub caches 173 else this._subCacheCount = 0; 174 } 175 }, 176 177 178 /** 179 * Return texture of cc.SpriteBatchNode 180 * @return {cc.Texture2D} 181 */ 182 getTexture: null, 183 184 _getTextureForCanvas:function () { 185 return this._cacheTexture; 186 }, 187 188 /** 189 * don't call visit on it's children ( override visit of cc.Node ) 190 * @override 191 * @param {CanvasRenderingContext2D} ctx 192 */ 193 visit: null, 194 195 _visitForCanvas: function (ctx) { 196 var context = ctx || cc._renderContext; 197 // quick return if not visible 198 if (!this._visible) 199 return; 200 201 context.save(); 202 this.transform(ctx); 203 var i, locChildren = this._children; 204 205 if (this._cacheDirty) { 206 // 207 var eglViewer = cc.view; 208 eglViewer._setScaleXYForRenderTexture(); 209 //add dirty region 210 var locCacheContext = this._cacheContext, locCacheCanvas = this._cacheCanvas; 211 locCacheContext.clearRect(0, 0, locCacheCanvas.width, -locCacheCanvas.height); 212 locCacheContext.save(); 213 locCacheContext.translate(this._anchorPointInPoints.x, -(this._anchorPointInPoints.y)); 214 if (locChildren) { 215 this.sortAllChildren(); 216 for (i = 0; i < locChildren.length; i++) { 217 if (locChildren[i]) 218 locChildren[i].visit(locCacheContext); 219 } 220 } 221 locCacheContext.restore(); 222 // Update sub caches if needed 223 if(this._subCacheCount > 0) { 224 var subCacheW = this._subCacheWidth, subCacheH = locCacheCanvas.height; 225 for(i = 0; i < this._subCacheCount; i++) { 226 this._subCacheContext[i].drawImage(locCacheCanvas, i * subCacheW, 0, subCacheW, subCacheH, 0, 0, subCacheW, subCacheH); 227 } 228 } 229 230 //reset Scale 231 eglViewer._resetScale(); 232 this._cacheDirty = false; 233 } 234 // draw RenderTexture 235 this.draw(ctx); 236 context.restore(); 237 }, 238 239 /** 240 * draw cc.SpriteBatchNode (override draw of cc.Node) 241 * @param {CanvasRenderingContext2D} ctx 242 */ 243 draw:null, 244 245 _drawForCanvas:function (ctx) { 246 var context = ctx || cc._renderContext; 247 //context.globalAlpha = this._opacity / 255; 248 var posX = 0 | ( -this._anchorPointInPoints.x), posY = 0 | ( -this._anchorPointInPoints.y); 249 var eglViewer = cc.view; 250 var locCacheCanvas = this._cacheCanvas; 251 //direct draw image by canvas drawImage 252 if (locCacheCanvas) { 253 var locSubCacheCount = this._subCacheCount, locCanvasHeight = locCacheCanvas.height * eglViewer._scaleY; 254 if(locSubCacheCount > 0) { 255 var locSubCacheCanvasArr = this._subCacheCanvas; 256 for(var i = 0; i < locSubCacheCount; i++){ 257 var selSubCanvas = locSubCacheCanvasArr[i]; 258 context.drawImage(locSubCacheCanvasArr[i], 0, 0, selSubCanvas.width, selSubCanvas.height, 259 posX + i * this._subCacheWidth, -(posY + locCanvasHeight), selSubCanvas.width * eglViewer._scaleX, locCanvasHeight); 260 } 261 } else{ 262 //context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height, 263 // posX, -(posY + locCacheCanvas.height ), locCacheCanvas.width, locCacheCanvas.height ); 264 context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height, 265 posX, -(posY + locCanvasHeight), locCacheCanvas.width * eglViewer._scaleX, locCanvasHeight); 266 } 267 } 268 }, 269 270 /** 271 * @return {cc.Size} 272 */ 273 getLayerSize:function () { 274 return cc.size(this._layerSize.width, this._layerSize.height); 275 }, 276 277 /** 278 * @param {cc.Size} Var 279 */ 280 setLayerSize:function (Var) { 281 this._layerSize.width = Var.width; 282 this._layerSize.height = Var.height; 283 }, 284 285 _getLayerWidth: function () { 286 return this._layerSize.width; 287 }, 288 _setLayerWidth: function (width) { 289 this._layerSize.width = width; 290 }, 291 _getLayerHeight: function () { 292 return this._layerSize.height; 293 }, 294 _setLayerHeight: function (height) { 295 this._layerSize.height = height; 296 }, 297 298 /** 299 * Size of the map's tile (could be different from the tile's size) 300 * @return {cc.Size} 301 */ 302 getMapTileSize:function () { 303 return cc.size(this._mapTileSize.width,this._mapTileSize.height); 304 }, 305 306 /** 307 * @param {cc.Size} Var 308 */ 309 setMapTileSize:function (Var) { 310 this._mapTileSize.width = Var.width; 311 this._mapTileSize.height = Var.height; 312 }, 313 314 _getTileWidth: function () { 315 return this._mapTileSize.width; 316 }, 317 _setTileWidth: function (width) { 318 this._mapTileSize.width = width; 319 }, 320 _getTileHeight: function () { 321 return this._mapTileSize.height; 322 }, 323 _setTileHeight: function (height) { 324 this._mapTileSize.height = height; 325 }, 326 327 /** 328 * Pointer to the map of tiles 329 * @return {Array} 330 */ 331 getTiles:function () { 332 return this.tiles; 333 }, 334 335 /** 336 * @param {Array} Var 337 */ 338 setTiles:function (Var) { 339 this.tiles = Var; 340 }, 341 342 /** 343 * Tile set information for the layer 344 * @return {cc.TMXTilesetInfo} 345 */ 346 getTileset:function () { 347 return this.tileset; 348 }, 349 350 /** 351 * @param {cc.TMXTilesetInfo} Var 352 */ 353 setTileset:function (Var) { 354 this.tileset = Var; 355 }, 356 357 /** 358 * Layer orientation, which is the same as the map orientation 359 * @return {Number} 360 */ 361 getLayerOrientation:function () { 362 return this.layerOrientation; 363 }, 364 365 /** 366 * @param {Number} Var 367 */ 368 setLayerOrientation:function (Var) { 369 this.layerOrientation = Var; 370 }, 371 372 /** 373 * properties from the layer. They can be added using Tiled 374 * @return {Array} 375 */ 376 getProperties:function () { 377 return this.properties; 378 }, 379 380 /** 381 * @param {Array} Var 382 */ 383 setProperties:function (Var) { 384 this.properties = Var; 385 }, 386 387 /** 388 * Initializes a cc.TMXLayer with a tileset info, a layer info and a map info 389 * @param {cc.TMXTilesetInfo} tilesetInfo 390 * @param {cc.TMXLayerInfo} layerInfo 391 * @param {cc.TMXMapInfo} mapInfo 392 * @return {Boolean} 393 */ 394 initWithTilesetInfo:function (tilesetInfo, layerInfo, mapInfo) { 395 // XXX: is 35% a good estimate ? 396 var size = layerInfo._layerSize; 397 var totalNumberOfTiles = parseInt(size.width * size.height); 398 var capacity = totalNumberOfTiles * 0.35 + 1; // 35 percent is occupied ? 399 var texture; 400 if (tilesetInfo) 401 texture = cc.textureCache.addImage(tilesetInfo.sourceImage); 402 403 if (this.initWithTexture(texture, capacity)) { 404 // layerInfo 405 this.layerName = layerInfo.name; 406 this._layerSize = size; 407 this.tiles = layerInfo._tiles; 408 this._minGID = layerInfo._minGID; 409 this._maxGID = layerInfo._maxGID; 410 this._opacity = layerInfo._opacity; 411 this.properties = layerInfo.properties; 412 this._contentScaleFactor = cc.director.getContentScaleFactor(); 413 414 // tilesetInfo 415 this.tileset = tilesetInfo; 416 417 // mapInfo 418 this._mapTileSize = mapInfo.getTileSize(); 419 this.layerOrientation = mapInfo.orientation; 420 421 // offset (after layer orientation is set); 422 var offset = this._calculateLayerOffset(layerInfo.offset); 423 this.setPosition(cc.pointPixelsToPoints(offset)); 424 425 this._atlasIndexArray = []; 426 this.setContentSize(cc.sizePixelsToPoints(cc.size(this._layerSize.width * this._mapTileSize.width, 427 this._layerSize.height * this._mapTileSize.height))); 428 this._useAutomaticVertexZ = false; 429 this._vertexZvalue = 0; 430 return true; 431 } 432 return false; 433 }, 434 435 /** 436 * <p>Dealloc the map that contains the tile position from memory. <br /> 437 * Unless you want to know at runtime the tiles positions, you can safely call this method. <br /> 438 * If you are going to call layer.getTileGIDAt() then, don't release the map</p> 439 */ 440 releaseMap:function () { 441 if (this.tiles) 442 this.tiles = null; 443 444 if (this._atlasIndexArray) 445 this._atlasIndexArray = null; 446 }, 447 448 /** 449 * <p>Returns the tile (cc.Sprite) at a given a tile coordinate. <br/> 450 * The returned cc.Sprite will be already added to the cc.TMXLayer. Don't add it again.<br/> 451 * The cc.Sprite can be treated like any other cc.Sprite: rotated, scaled, translated, opacity, color, etc. <br/> 452 * You can remove either by calling: <br/> 453 * - layer.removeChild(sprite, cleanup); <br/> 454 * - or layer.removeTileAt(ccp(x,y)); </p> 455 * @param {cc.Point} pos 456 * @return {cc.Sprite} 457 */ 458 getTileAt: function (pos) { 459 if(!pos) 460 throw "cc.TMXLayer.getTileAt(): pos should be non-null"; 461 if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) 462 throw "cc.TMXLayer.getTileAt(): invalid position"; 463 if(!this.tiles || !this._atlasIndexArray){ 464 cc.log("cc.TMXLayer.getTileAt(): TMXLayer: the tiles map has been released"); 465 return null; 466 } 467 468 var tile = null; 469 var gid = this.getTileGIDAt(pos); 470 471 // if GID == 0, then no tile is present 472 if (gid === 0) 473 return tile; 474 475 var z = 0 | (pos.x + pos.y * this._layerSize.width); 476 tile = this.getChildByTag(z); 477 // tile not created yet. create it 478 if (!tile) { 479 var rect = this.tileset.rectForGID(gid); 480 rect = cc.rectPixelsToPoints(rect); 481 482 tile = new cc.Sprite(); 483 tile.initWithTexture(this.texture, rect); 484 tile.batchNode = this; 485 tile.setPosition(this.getPositionAt(pos)); 486 tile.vertexZ = this._vertexZForPos(pos); 487 tile.anchorX = 0; 488 tile.anchorY = 0; 489 tile.opacity = this._opacity; 490 491 var indexForZ = this._atlasIndexForExistantZ(z); 492 this.addSpriteWithoutQuad(tile, indexForZ, z); 493 } 494 return tile; 495 }, 496 497 /** 498 * Returns the tile gid at a given tile coordinate. <br /> 499 * if it returns 0, it means that the tile is empty. <br /> 500 * This method requires the the tile map has not been previously released (eg. don't call layer.releaseMap())<br /> 501 * @param {cc.Point} pos 502 * @return {Number} 503 */ 504 getTileGIDAt:function (pos) { 505 if(!pos) 506 throw "cc.TMXLayer.getTileGIDAt(): pos should be non-null"; 507 if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) 508 throw "cc.TMXLayer.getTileGIDAt(): invalid position"; 509 if(!this.tiles || !this._atlasIndexArray){ 510 cc.log("cc.TMXLayer.getTileGIDAt(): TMXLayer: the tiles map has been released"); 511 return null; 512 } 513 514 var idx = 0 | (pos.x + pos.y * this._layerSize.width); 515 // Bits on the far end of the 32-bit global tile ID are used for tile flags 516 var tile = this.tiles[idx]; 517 518 return (tile & cc.TMX_TILE_FLIPPED_MASK) >>> 0; 519 }, 520 // XXX: deprecated 521 // tileGIDAt:getTileGIDAt, 522 523 /** 524 * lipped tiles can be changed dynamically 525 * @param {cc.Point} pos 526 * @return {Number} 527 */ 528 getTileFlagsAt:function (pos) { 529 if(!pos) 530 throw "cc.TMXLayer.getTileFlagsAt(): pos should be non-null"; 531 if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) 532 throw "cc.TMXLayer.getTileFlagsAt(): invalid position"; 533 if(!this.tiles || !this._atlasIndexArray){ 534 cc.log("cc.TMXLayer.getTileFlagsAt(): TMXLayer: the tiles map has been released"); 535 return null; 536 } 537 538 var idx = 0 | (pos.x + pos.y * this._layerSize.width); 539 // Bits on the far end of the 32-bit global tile ID are used for tile flags 540 var tile = this.tiles[idx]; 541 542 return (tile & cc.TMX_TILE_FLIPPED_ALL) >>> 0; 543 }, 544 // XXX: deprecated 545 // tileFlagAt:getTileFlagsAt, 546 547 /** 548 * <p>Sets the tile gid (gid = tile global id) at a given tile coordinate.<br /> 549 * The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor . Tileset Mgr +1.<br /> 550 * If a tile is already placed at that position, then it will be removed.</p> 551 * @param {Number} gid 552 * @param {cc.Point} pos 553 * @param {Number} flags 554 */ 555 setTileGID:function (gid, pos, flags) { 556 if(!pos) 557 throw "cc.TMXLayer.setTileGID(): pos should be non-null"; 558 if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) 559 throw "cc.TMXLayer.setTileGID(): invalid position"; 560 if(!this.tiles || !this._atlasIndexArray){ 561 cc.log("cc.TMXLayer.setTileGID(): TMXLayer: the tiles map has been released"); 562 return null; 563 } 564 if(gid !== 0 && gid < this.tileset.firstGid){ 565 cc.log( "cc.TMXLayer.setTileGID(): invalid gid:" + gid); 566 return null; 567 } 568 569 flags = flags || 0; 570 this._setNodeDirtyForCache(); 571 572 var currentFlags = this.getTileFlagsAt(pos); 573 var currentGID = this.getTileGIDAt(pos); 574 575 if (currentGID != gid || currentFlags != flags) { 576 var gidAndFlags = (gid | flags) >>> 0; 577 // setting gid=0 is equal to remove the tile 578 if (gid === 0) 579 this.removeTileAt(pos); 580 else if (currentGID === 0) // empty tile. create a new one 581 this._insertTileForGID(gidAndFlags, pos); 582 else { // modifying an existing tile with a non-empty tile 583 var z = pos.x + pos.y * this._layerSize.width; 584 var sprite = this.getChildByTag(z); 585 if (sprite) { 586 var rect = this.tileset.rectForGID(gid); 587 rect = cc.rectPixelsToPoints(rect); 588 589 sprite.setTextureRect(rect, false); 590 if (flags != null) 591 this._setupTileSprite(sprite, pos, gidAndFlags); 592 593 this.tiles[z] = gidAndFlags; 594 } else 595 this._updateTileForGID(gidAndFlags, pos); 596 } 597 } 598 }, 599 600 /** 601 * Removes a tile at given tile coordinate 602 * @param {cc.Point} pos 603 */ 604 removeTileAt:function (pos) { 605 if(!pos) 606 throw "cc.TMXLayer.removeTileAt(): pos should be non-null"; 607 if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) 608 throw "cc.TMXLayer.removeTileAt(): invalid position"; 609 if(!this.tiles || !this._atlasIndexArray){ 610 cc.log("cc.TMXLayer.removeTileAt(): TMXLayer: the tiles map has been released"); 611 return null; 612 } 613 614 var gid = this.getTileGIDAt(pos); 615 if (gid !== 0) { 616 if (cc._renderType === cc._RENDER_TYPE_CANVAS) 617 this._setNodeDirtyForCache(); 618 var z = 0 | (pos.x + pos.y * this._layerSize.width); 619 var atlasIndex = this._atlasIndexForExistantZ(z); 620 // remove tile from GID map 621 this.tiles[z] = 0; 622 623 // remove tile from atlas position array 624 this._atlasIndexArray.splice(atlasIndex, 1); 625 626 // remove it from sprites and/or texture atlas 627 var sprite = this.getChildByTag(z); 628 629 if (sprite) 630 cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, true); //this.removeChild(sprite, true); 631 else { 632 if(cc._renderType === cc._RENDER_TYPE_WEBGL) 633 this.textureAtlas.removeQuadAtIndex(atlasIndex); 634 635 // update possible children 636 if (this._children) { 637 var locChildren = this._children; 638 for (var i = 0, len = locChildren.length; i < len; i++) { 639 var child = locChildren[i]; 640 if (child) { 641 var ai = child.atlasIndex; 642 if (ai >= atlasIndex) 643 child.atlasIndex = ai - 1; 644 } 645 } 646 } 647 } 648 } 649 }, 650 651 /** 652 * Returns the position in pixels of a given tile coordinate 653 * @param {cc.Point} pos 654 * @return {cc.Point} 655 */ 656 getPositionAt:function (pos) { 657 var ret = cc.p(0,0); 658 switch (this.layerOrientation) { 659 case cc.TMX_ORIENTATION_ORTHO: 660 ret = this._positionForOrthoAt(pos); 661 break; 662 case cc.TMX_ORIENTATION_ISO: 663 ret = this._positionForIsoAt(pos); 664 break; 665 case cc.TMX_ORIENTATION_HEX: 666 ret = this._positionForHexAt(pos); 667 break; 668 } 669 return cc.pointPixelsToPoints(ret); 670 }, 671 // XXX: Deprecated. For backward compatibility only 672 // positionAt:getPositionAt, 673 674 /** 675 * Return the value for the specific property name 676 * @param {String} propertyName 677 * @return {*} 678 */ 679 getProperty:function (propertyName) { 680 return this.properties[propertyName]; 681 }, 682 683 /** 684 * Creates the tiles 685 */ 686 setupTiles:function () { 687 // Optimization: quick hack that sets the image size on the tileset 688 if (cc._renderType === cc._RENDER_TYPE_CANVAS) { 689 this.tileset.imageSize = this._originalTexture.getContentSizeInPixels(); 690 } else { 691 this.tileset.imageSize = this.textureAtlas.texture.getContentSizeInPixels(); 692 693 // By default all the tiles are aliased 694 // pros: 695 // - easier to render 696 // cons: 697 // - difficult to scale / rotate / etc. 698 this.textureAtlas.texture.setAliasTexParameters(); 699 } 700 701 // Parse cocos2d properties 702 this._parseInternalProperties(); 703 if (cc._renderType === cc._RENDER_TYPE_CANVAS) 704 this._setNodeDirtyForCache(); 705 706 var locLayerHeight = this._layerSize.height, locLayerWidth = this._layerSize.width; 707 for (var y = 0; y < locLayerHeight; y++) { 708 for (var x = 0; x < locLayerWidth; x++) { 709 var pos = x + locLayerWidth * y; 710 var gid = this.tiles[pos]; 711 712 // XXX: gid == 0 -. empty tile 713 if (gid !== 0) { 714 this._appendTileForGID(gid, cc.p(x, y)); 715 // Optimization: update min and max GID rendered by the layer 716 this._minGID = Math.min(gid, this._minGID); 717 this._maxGID = Math.max(gid, this._maxGID); 718 } 719 } 720 } 721 722 if (!((this._maxGID >= this.tileset.firstGid) && (this._minGID >= this.tileset.firstGid))) { 723 cc.log("cocos2d:TMX: Only 1 tileset per layer is supported"); 724 } 725 }, 726 727 /** 728 * cc.TMXLayer doesn't support adding a cc.Sprite manually. 729 * @warning addChild(child); is not supported on cc.TMXLayer. Instead of setTileGID. 730 * @param {cc.Node} child 731 * @param {number} zOrder 732 * @param {number} tag 733 */ 734 addChild:function (child, zOrder, tag) { 735 cc.log("addChild: is not supported on cc.TMXLayer. Instead use setTileGID or tileAt."); 736 }, 737 738 /** 739 * Remove child 740 * @param {cc.Sprite} sprite 741 * @param {Boolean} cleanup 742 */ 743 removeChild:function (sprite, cleanup) { 744 // allows removing nil objects 745 if (!sprite) 746 return; 747 748 if(this._children.indexOf(sprite) === -1){ 749 cc.log("cc.TMXLayer.removeChild(): Tile does not belong to TMXLayer"); 750 return; 751 } 752 753 if (cc._renderType === cc._RENDER_TYPE_CANVAS) 754 this._setNodeDirtyForCache(); 755 var atlasIndex = sprite.atlasIndex; 756 var zz = this._atlasIndexArray[atlasIndex]; 757 this.tiles[zz] = 0; 758 this._atlasIndexArray.splice(atlasIndex, 1); 759 cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, cleanup); 760 }, 761 762 /** 763 * @return {String} 764 */ 765 getLayerName:function () { 766 return this.layerName; 767 }, 768 769 /** 770 * @param {String} layerName 771 */ 772 setLayerName:function (layerName) { 773 this.layerName = layerName; 774 }, 775 776 _positionForIsoAt:function (pos) { 777 return cc.p(this._mapTileSize.width / 2 * ( this._layerSize.width + pos.x - pos.y - 1), 778 this._mapTileSize.height / 2 * (( this._layerSize.height * 2 - pos.x - pos.y) - 2)); 779 }, 780 781 _positionForOrthoAt:function (pos) { 782 return cc.p(pos.x * this._mapTileSize.width, 783 (this._layerSize.height - pos.y - 1) * this._mapTileSize.height); 784 }, 785 786 _positionForHexAt:function (pos) { 787 var diffY = (pos.x % 2 == 1) ? (-this._mapTileSize.height / 2) : 0; 788 return cc.p(pos.x * this._mapTileSize.width * 3 / 4, 789 (this._layerSize.height - pos.y - 1) * this._mapTileSize.height + diffY); 790 }, 791 792 _calculateLayerOffset:function (pos) { 793 var ret = cc.p(0,0); 794 switch (this.layerOrientation) { 795 case cc.TMX_ORIENTATION_ORTHO: 796 ret = cc.p(pos.x * this._mapTileSize.width, -pos.y * this._mapTileSize.height); 797 break; 798 case cc.TMX_ORIENTATION_ISO: 799 ret = cc.p((this._mapTileSize.width / 2) * (pos.x - pos.y), 800 (this._mapTileSize.height / 2 ) * (-pos.x - pos.y)); 801 break; 802 case cc.TMX_ORIENTATION_HEX: 803 if(pos.x !== 0 || pos.y !== 0) 804 cc.log("offset for hexagonal map not implemented yet"); 805 break; 806 } 807 return ret; 808 }, 809 810 _appendTileForGID:function (gid, pos) { 811 var rect = this.tileset.rectForGID(gid); 812 rect = cc.rectPixelsToPoints(rect); 813 814 var z = 0 | (pos.x + pos.y * this._layerSize.width); 815 var tile = this._reusedTileWithRect(rect); 816 this._setupTileSprite(tile, pos, gid); 817 818 // optimization: 819 // The difference between appendTileForGID and insertTileforGID is that append is faster, since 820 // it appends the tile at the end of the texture atlas 821 var indexForZ = this._atlasIndexArray.length; 822 823 // don't add it using the "standard" way. 824 this.insertQuadFromSprite(tile, indexForZ); 825 826 // append should be after addQuadFromSprite since it modifies the quantity values 827 this._atlasIndexArray.splice(indexForZ, 0, z); 828 return tile; 829 }, 830 831 _insertTileForGID:function (gid, pos) { 832 var rect = this.tileset.rectForGID(gid); 833 rect = cc.rectPixelsToPoints(rect); 834 835 var z = 0 | (pos.x + pos.y * this._layerSize.width); 836 var tile = this._reusedTileWithRect(rect); 837 this._setupTileSprite(tile, pos, gid); 838 839 // get atlas index 840 var indexForZ = this._atlasIndexForNewZ(z); 841 842 // Optimization: add the quad without adding a child 843 this.insertQuadFromSprite(tile, indexForZ); 844 845 // insert it into the local atlasindex array 846 this._atlasIndexArray.splice(indexForZ, 0, z); 847 // update possible children 848 if (this._children) { 849 var locChildren = this._children; 850 for (var i = 0, len = locChildren.length; i < len; i++) { 851 var child = locChildren[i]; 852 if (child) { 853 var ai = child.atlasIndex; 854 if (ai >= indexForZ) 855 child.atlasIndex = ai + 1; 856 } 857 } 858 } 859 this.tiles[z] = gid; 860 return tile; 861 }, 862 863 _updateTileForGID:function (gid, pos) { 864 var rect = this.tileset.rectForGID(gid); 865 var locScaleFactor = this._contentScaleFactor; 866 rect = cc.rect(rect.x / locScaleFactor, rect.y / locScaleFactor, 867 rect.width / locScaleFactor, rect.height / locScaleFactor); 868 var z = pos.x + pos.y * this._layerSize.width; 869 870 var tile = this._reusedTileWithRect(rect); 871 this._setupTileSprite(tile, pos, gid); 872 873 // get atlas index 874 var indexForZ = this._atlasIndexForExistantZ(z); 875 tile.atlasIndex = indexForZ; 876 tile.dirty = true; 877 tile.updateTransform(); 878 this.tiles[z] = gid; 879 880 return tile; 881 }, 882 883 //The layer recognizes some special properties, like cc_vertez 884 _parseInternalProperties:function () { 885 // if cc_vertex=automatic, then tiles will be rendered using vertexz 886 var vertexz = this.getProperty("cc_vertexz"); 887 if (vertexz) { 888 if (vertexz == "automatic") { 889 this._useAutomaticVertexZ = true; 890 var alphaFuncVal = this.getProperty("cc_alpha_func"); 891 var alphaFuncValue = 0; 892 if (alphaFuncVal) 893 alphaFuncValue = parseFloat(alphaFuncVal); 894 895 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 896 this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST); 897 var alphaValueLocation = cc._renderContext.getUniformLocation(this.shaderProgram.getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S); 898 // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison 899 this.shaderProgram.use(); 900 this.shaderProgram.setUniformLocationWith1f(alphaValueLocation, alphaFuncValue); 901 } 902 } else 903 this._vertexZvalue = parseInt(vertexz, 10); 904 } 905 }, 906 907 _setupTileSprite:function (sprite, pos, gid) { 908 var z = pos.x + pos.y * this._layerSize.width; 909 sprite.setPosition(this.getPositionAt(pos)); 910 if (cc._renderType === cc._RENDER_TYPE_WEBGL) 911 sprite.vertexZ = this._vertexZForPos(pos); 912 else 913 sprite.tag = z; 914 915 sprite.anchorX = 0; 916 sprite.anchorY = 0; 917 sprite.opacity = this._opacity; 918 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 919 sprite.rotation = 0.0; 920 } 921 922 sprite.setFlippedX(false); 923 sprite.setFlippedY(false); 924 925 // Rotation in tiled is achieved using 3 flipped states, flipping across the horizontal, vertical, and diagonal axes of the tiles. 926 if ((gid & cc.TMX_TILE_DIAGONAL_FLAG) >>> 0) { 927 // put the anchor in the middle for ease of rotation. 928 sprite.anchorX = 0.5; 929 sprite.anchorY = 0.5; 930 sprite.x = this.getPositionAt(pos).x + sprite.width / 2; 931 sprite.y = this.getPositionAt(pos).y + sprite.height / 2; 932 933 var flag = (gid & (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG) >>> 0) >>> 0; 934 // handle the 4 diagonally flipped states. 935 if (flag == cc.TMX_TILE_HORIZONTAL_FLAG) 936 sprite.rotation = 90; 937 else if (flag == cc.TMX_TILE_VERTICAL_FLAG) 938 sprite.rotation = 270; 939 else if (flag == (cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) { 940 sprite.rotation = 90; 941 sprite.setFlippedX(true); 942 } else { 943 sprite.rotation = 270; 944 sprite.setFlippedX(true); 945 } 946 } else { 947 if ((gid & cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) { 948 sprite.setFlippedX(true); 949 } 950 951 if ((gid & cc.TMX_TILE_VERTICAL_FLAG) >>> 0) { 952 sprite.setFlippedY(true); 953 } 954 } 955 }, 956 957 _reusedTileWithRect:function (rect) { 958 if(cc._renderType === cc._RENDER_TYPE_WEBGL){ 959 if (!this._reusedTile) { 960 this._reusedTile = new cc.Sprite(); 961 this._reusedTile.initWithTexture(this.texture, rect, false); 962 this._reusedTile.batchNode = this; 963 } else { 964 // XXX HACK: Needed because if "batch node" is nil, 965 // then the Sprite'squad will be reset 966 this._reusedTile.batchNode = null; 967 968 // Re-init the sprite 969 this._reusedTile.setTextureRect(rect, false); 970 971 // restore the batch node 972 this._reusedTile.batchNode = this; 973 } 974 } else { 975 this._reusedTile = new cc.Sprite(); 976 this._reusedTile.initWithTexture(this._textureForCanvas, rect, false); 977 this._reusedTile.batchNode = this; 978 this._reusedTile.parent = this; 979 } 980 return this._reusedTile; 981 }, 982 983 _vertexZForPos:function (pos) { 984 var ret = 0; 985 var maxVal = 0; 986 if (this._useAutomaticVertexZ) { 987 switch (this.layerOrientation) { 988 case cc.TMX_ORIENTATION_ISO: 989 maxVal = this._layerSize.width + this._layerSize.height; 990 ret = -(maxVal - (pos.x + pos.y)); 991 break; 992 case cc.TMX_ORIENTATION_ORTHO: 993 ret = -(this._layerSize.height - pos.y); 994 break; 995 case cc.TMX_ORIENTATION_HEX: 996 cc.log("TMX Hexa zOrder not supported"); 997 break; 998 default: 999 cc.log("TMX invalid value"); 1000 break; 1001 } 1002 } else 1003 ret = this._vertexZvalue; 1004 return ret; 1005 }, 1006 1007 _atlasIndexForExistantZ:function (z) { 1008 var item; 1009 if (this._atlasIndexArray) { 1010 var locAtlasIndexArray = this._atlasIndexArray; 1011 for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) { 1012 item = locAtlasIndexArray[i]; 1013 if (item == z) 1014 break; 1015 } 1016 } 1017 if(typeof item != "number") 1018 cc.log("cc.TMXLayer._atlasIndexForExistantZ(): TMX atlas index not found. Shall not happen"); 1019 return i; 1020 }, 1021 1022 _atlasIndexForNewZ:function (z) { 1023 var locAtlasIndexArray = this._atlasIndexArray; 1024 for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) { 1025 var val = locAtlasIndexArray[i]; 1026 if (z < val) 1027 break; 1028 } 1029 return i; 1030 } 1031 }); 1032 1033 var _p = cc.TMXLayer.prototype; 1034 1035 if(cc._renderType == cc._RENDER_TYPE_WEBGL){ 1036 _p.draw = cc.SpriteBatchNode.prototype.draw; 1037 _p.visit = cc.SpriteBatchNode.prototype.visit; 1038 _p.getTexture = cc.SpriteBatchNode.prototype.getTexture; 1039 }else{ 1040 _p.draw = _p._drawForCanvas; 1041 _p.visit = _p._visitForCanvas; 1042 _p.getTexture = _p._getTextureForCanvas; 1043 } 1044 1045 /** @expose */ 1046 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); 1047 1048 // Extended properties 1049 /** @expose */ 1050 _p.layerWidth; 1051 cc.defineGetterSetter(_p, "layerWidth", _p._getLayerWidth, _p._setLayerWidth); 1052 /** @expose */ 1053 _p.layerHeight; 1054 cc.defineGetterSetter(_p, "layerHeight", _p._getLayerHeight, _p._setLayerHeight); 1055 /** @expose */ 1056 _p.tileWidth; 1057 cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth); 1058 /** @expose */ 1059 _p.tileHeight; 1060 cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight); 1061 1062 1063 /** 1064 * Creates a cc.TMXLayer with an tile set info, a layer info and a map info 1065 * @param {cc.TMXTilesetInfo} tilesetInfo 1066 * @param {cc.TMXLayerInfo} layerInfo 1067 * @param {cc.TMXMapInfo} mapInfo 1068 * @return {cc.TMXLayer|Null} 1069 */ 1070 cc.TMXLayer.create = function (tilesetInfo, layerInfo, mapInfo) { 1071 return new cc.TMXLayer(tilesetInfo, layerInfo, mapInfo); 1072 }; 1073