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