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