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