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