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