1 /**************************************************************************** 2 Copyright (c) 2013-2014 Chukong Technologies Inc. 3 4 http://www.cocos2d-x.org 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 ****************************************************************************/ 24 25 /** 26 * A class inhert from cc.Node, use for saving some protected children in other list. 27 * @class 28 * @extends cc.Node 29 */ 30 cc.ProtectedNode = cc.Node.extend(/** @lends cc.ProtectedNode# */{ 31 _protectedChildren: null, 32 _reorderProtectedChildDirty: false, 33 34 _insertProtectedChild: function(child, z){ 35 this._reorderProtectedChildDirty = true; 36 this._protectedChildren.push(child); 37 child._setLocalZOrder(z); 38 }, 39 40 /** 41 * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. 42 * @function 43 */ 44 ctor: function(){ 45 cc.Node.prototype.ctor.call(this); 46 this._protectedChildren = []; 47 }, 48 49 /** 50 * <p> 51 * Adds a child to the container with z order and tag <br/> 52 * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately. <br/> 53 * </p> 54 * @param {cc.Node} child A child node 55 * @param {Number} [localZOrder] Z order for drawing priority. Please refer to `setLocalZOrder(int)` 56 * @param {Number} [tag] An integer to identify the node easily. Please refer to `setTag(int)` 57 */ 58 addProtectedChild: function(child, localZOrder, tag){ 59 cc.assert(child != null, "child must be non-nil"); 60 cc.assert(!child.parent, "child already added. It can't be added again"); 61 62 localZOrder = localZOrder || child.getLocalZOrder(); 63 if(tag) 64 child.setTag(tag); 65 66 this._insertProtectedChild(child, localZOrder); 67 child.setParent(this); 68 child.setOrderOfArrival(cc.s_globalOrderOfArrival); 69 70 //TODO USE PHYSICS 71 if(this._running){ 72 child.onEnter(); 73 // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter 74 if(this._isTransitionFinished) 75 child.onEnterTransitionDidFinish(); 76 } 77 if(this._cascadeColorEnabled) 78 this._enableCascadeColor(); 79 if (this._cascadeOpacityEnabled) 80 this._enableCascadeOpacity(); 81 }, 82 83 /** 84 * Gets a child from the container with its tag 85 * @param {Number} tag An identifier to find the child node. 86 * @return {cc.Node} a Node object whose tag equals to the input parameter 87 */ 88 getProtectedChildByTag: function(tag){ 89 cc.assert(tag != cc.NODE_TAG_INVALID, "Invalid tag"); 90 var locChildren = this._protectedChildren; 91 for(var i = 0, len = locChildren.length; i < len; i++) 92 if(locChildren.getTag() == tag) 93 return locChildren[i]; 94 return null; 95 }, 96 97 /** 98 * Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter. 99 * @param {cc.Node} child The child node which will be removed. 100 * @param {Boolean} [cleanup=true] true if all running actions and callbacks on the child node will be cleanup, false otherwise. 101 */ 102 removeProtectedChild: function(child, cleanup){ 103 if(cleanup == null) 104 cleanup = true; 105 var locChildren = this._protectedChildren; 106 if(locChildren.length === 0) 107 return; 108 var idx = locChildren.indexOf(child); 109 if(idx > -1){ 110 if(this._running){ 111 child.onExitTransitionDidStart(); 112 child.onExit(); 113 } 114 //TODO USE PHYSICS 115 116 // If you don't do cleanup, the child's actions will not get removed and the 117 // its scheduledSelectors_ dict will not get released! 118 if (cleanup) 119 child.cleanup(); 120 121 // set parent nil at the end 122 child.setParent(null); 123 locChildren.splice(idx, 1); 124 } 125 }, 126 127 /** 128 * Removes a child from the container by tag value. <br/> 129 * It will also cleanup all running actions depending on the cleanup parameter 130 * @param {Number} tag 131 * @param {Boolean} [cleanup=true] 132 */ 133 removeProtectedChildByTag: function(tag, cleanup){ 134 cc.assert( tag != cc.NODE_TAG_INVALID, "Invalid tag"); 135 136 if(cleanup == null) 137 cleanup = true; 138 139 var child = this.getProtectedChildByTag(tag); 140 141 if (child == null) 142 cc.log("cocos2d: removeChildByTag(tag = %d): child not found!", tag); 143 else 144 this.removeProtectedChild(child, cleanup); 145 }, 146 147 /** 148 * Removes all children from the container with a cleanup. 149 * @see cc.ProtectedNode#removeAllProtectedChildrenWithCleanup 150 */ 151 removeAllProtectedChildren: function(){ 152 this.removeAllProtectedChildrenWithCleanup(true); 153 }, 154 155 /** 156 * Removes all children from the container, and do a cleanup to all running actions depending on the cleanup parameter. 157 * @param {Boolean} [cleanup=true] true if all running actions on all children nodes should be cleanup, false otherwise. 158 */ 159 removeAllProtectedChildrenWithCleanup: function(cleanup){ 160 if(cleanup == null) 161 cleanup = true; 162 var locChildren = this._protectedChildren; 163 // not using detachChild improves speed here 164 for (var i = 0, len = locChildren.length; i< len; i++) { 165 var child = locChildren[i]; 166 // IMPORTANT: 167 // -1st do onExit 168 // -2nd cleanup 169 if(this._running){ 170 child.onExitTransitionDidStart(); 171 child.onExit(); 172 } 173 174 //TODO USE PHYSICS 175 if (cleanup) 176 child.cleanup(); 177 // set parent nil at the end 178 child.setParent(null); 179 } 180 locChildren.length = 0; 181 }, 182 183 /** 184 * Reorders a child according to a new z value. 185 * @param {cc.Node} child An already added child node. It MUST be already added. 186 * @param {Number} localZOrder Z order for drawing priority. Please refer to setLocalZOrder(int) 187 */ 188 reorderProtectedChild: function(child, localZOrder){ 189 cc.assert( child != null, "Child must be non-nil"); 190 this._reorderProtectedChildDirty = true; 191 child.setOrderOfArrival(cc.s_globalOrderOfArrival++); 192 child._setLocalZOrder(localZOrder); 193 }, 194 195 /** 196 * <p> 197 * Sorts the children array once before drawing, instead of every time when a child is added or reordered. <br/> 198 * This approach can improves the performance massively. <br/> 199 * @note Don't call this manually unless a child added needs to be removed in the same frame 200 * </p> 201 */ 202 sortAllProtectedChildren: function(){ 203 if (this._reorderProtectedChildDirty) { 204 var _children = this._protectedChildren; 205 206 // insertion sort 207 var len = _children.length, i, j, tmp; 208 for(i=1; i<len; i++){ 209 tmp = _children[i]; 210 j = i - 1; 211 212 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller 213 while(j >= 0){ 214 if(tmp._localZOrder < _children[j]._localZOrder){ 215 _children[j+1] = _children[j]; 216 }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){ 217 _children[j+1] = _children[j]; 218 }else 219 break; 220 j--; 221 } 222 _children[j+1] = tmp; 223 } 224 225 //don't need to check children recursively, that's done in visit of each child 226 this._reorderProtectedChildDirty = false; 227 } 228 }, 229 230 /** 231 * transforms and draws itself, and visit its children and protected children. 232 * @override 233 * @function 234 * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx context of renderer 235 */ 236 visit: null, 237 238 _visitForCanvas: function(ctx){ 239 var _t = this; 240 // quick return if not visible 241 if (!_t._visible) 242 return; 243 244 //visit for canvas 245 var context = ctx || cc._renderContext, i, j; 246 var children = _t._children, child; 247 var locChildren = _t._children, locProtectedChildren = this._protectedChildren; 248 var childLen = locChildren.length, pLen = locProtectedChildren.length; 249 context.save(); 250 _t.transform(context); 251 252 _t.sortAllChildren(); 253 _t.sortAllProtectedChildren(); 254 255 // draw children zOrder < 0 256 for (i = 0; i < childLen; i++) { 257 child = children[i]; 258 if (child._localZOrder < 0) 259 child.visit(context); 260 else 261 break; 262 } 263 for (j = 0; j < pLen; j++) { 264 child = locProtectedChildren[j]; 265 if (child._localZOrder < 0) 266 child.visit(context); 267 else 268 break; 269 } 270 271 _t.draw(context); 272 273 for (; i < childLen; i++) 274 children[i] && children[i].visit(context); 275 for (; j < pLen; j++) 276 locProtectedChildren[j] && locProtectedChildren[j].visit(context); 277 278 this._cacheDirty = false; 279 _t.arrivalOrder = 0; 280 context.restore(); 281 }, 282 283 _visitForWebGL: function(){ 284 var _t = this; 285 // quick return if not visible 286 if (!_t._visible) 287 return; 288 var context = cc._renderContext, i, currentStack = cc.current_stack, j; 289 290 //optimize performance for javascript 291 currentStack.stack.push(currentStack.top); 292 cc.kmMat4Assign(_t._stackMatrix, currentStack.top); 293 currentStack.top = _t._stackMatrix; 294 295 var locGrid = _t.grid; 296 if (locGrid && locGrid._active) 297 locGrid.beforeDraw(); 298 299 _t.transform(); 300 301 var locChildren = _t._children, locProtectedChildren = this._protectedChildren; 302 var childLen = locChildren.length, pLen = locProtectedChildren.length; 303 _t.sortAllChildren(); 304 _t.sortAllProtectedChildren(); 305 306 // draw children zOrder < 0 307 for (i = 0; i < childLen; i++) { 308 if (locChildren[i] && locChildren[i]._localZOrder < 0) 309 locChildren[i].visit(); 310 else 311 break; 312 } 313 for(j = 0; j < pLen; j++){ 314 if (locProtectedChildren[j] && locProtectedChildren[j]._localZOrder < 0) 315 locProtectedChildren[j].visit(); 316 else 317 break; 318 } 319 _t.draw(context); 320 // draw children zOrder >= 0 321 for (; i < childLen; i++) { 322 locChildren[i] && locChildren[i].visit(); 323 } 324 for (; j < pLen; j++) { 325 locProtectedChildren[j] && locProtectedChildren[j].visit(); 326 } 327 328 _t.arrivalOrder = 0; 329 if (locGrid && locGrid._active) 330 locGrid.afterDraw(_t); 331 332 //optimize performance for javascript 333 currentStack.top = currentStack.stack.pop(); 334 }, 335 336 /** 337 * Stops itself and its children and protected children's all running actions and schedulers 338 * @override 339 */ 340 cleanup: function(){ 341 cc.Node.prototype.cleanup.call(this); 342 var locChildren = this._protectedChildren; 343 for(var i = 0 , len = locChildren.length; i < len; i++) 344 locChildren[i].cleanup(); 345 }, 346 347 /** 348 * Calls its parent's onEnter and calls its protected children's onEnter 349 * @override 350 */ 351 onEnter: function(){ 352 cc.Node.prototype.onEnter.call(this); 353 var locChildren = this._protectedChildren; 354 for(var i = 0, len = locChildren.length;i< len;i++) 355 locChildren[i].onEnter(); 356 }, 357 358 /** 359 * <p> 360 * Event callback that is invoked when the Node enters in the 'stage'. <br/> 361 * If the Node enters the 'stage' with a transition, this event is called when the transition finishes. <br/> 362 * If you override onEnterTransitionDidFinish, you shall call its parent's one, e.g. Node::onEnterTransitionDidFinish() 363 * </p> 364 * @override 365 */ 366 onEnterTransitionDidFinish: function(){ 367 cc.Node.prototype.onEnterTransitionDidFinish.call(this); 368 var locChildren = this._protectedChildren; 369 for(var i = 0, len = locChildren.length;i< len;i++) 370 locChildren[i].onEnterTransitionDidFinish(); 371 }, 372 373 /** 374 * Calls its parent's onExit and calls its protected children's onExit 375 * @override 376 */ 377 onExit:function(){ 378 cc.Node.prototype.onExit.call(this); 379 var locChildren = this._protectedChildren; 380 for(var i = 0, len = locChildren.length;i< len;i++) 381 locChildren[i].onExit(); 382 }, 383 384 /** 385 * <p> 386 * Event callback that is called every time the Node leaves the 'stage'. <br/> 387 * If the Node leaves the 'stage' with a transition, this callback is called when the transition starts. 388 * </p> 389 */ 390 onExitTransitionDidStart: function(){ 391 cc.Node.prototype.onExitTransitionDidStart.call(this); 392 var locChildren = this._protectedChildren; 393 for(var i = 0, len = locChildren.length;i< len;i++) 394 locChildren[i].onExitTransitionDidStart(); 395 }, 396 397 /** 398 * Updates itself and its protected children displayed opacity, if opacity cascade is enable, its children also update. 399 * @param {Number} parentOpacity 400 * @override 401 */ 402 updateDisplayedOpacity: function(parentOpacity){ 403 this._displayedOpacity = this._realOpacity * parentOpacity/255.0; 404 this._updateColor(); 405 406 var i,len, locChildren, _opacity = this._displayedOpacity; 407 if (this._cascadeOpacityEnabled){ 408 locChildren = this._children; 409 for(i = 0, len = locChildren.length;i < len; i++){ 410 if(locChildren[i].updateDisplayedOpacity) 411 locChildren[i].updateDisplayedOpacity(_opacity); 412 } 413 } 414 locChildren = this._protectedChildren; 415 for(i = 0, len = locChildren.length;i < len; i++){ 416 if(locChildren[i]) 417 locChildren[i].updateDisplayedOpacity(_opacity); 418 } 419 }, 420 421 /** 422 * Updates itself and its protected children displayed color, if opacity cascade is enable, its children also update. 423 * @param {cc.Color} parentColor 424 * @override 425 */ 426 updateDisplayedColor: function(parentColor){ 427 var displayedColor = this._displayedColor, realColor = this._realColor; 428 displayedColor.r = realColor.r * parentColor.r/255.0; 429 displayedColor.g = realColor.g * parentColor.g/255.0; 430 displayedColor.b = realColor.b * parentColor.b/255.0; 431 this._updateColor(); 432 433 var i, len, locChildren; 434 if (this._cascadeColorEnabled){ 435 locChildren = this._children; 436 for(i = 0, len = locChildren.length; i < len; i++){ 437 if(locChildren[i].updateDisplayedColor) 438 locChildren[i].updateDisplayedColor(displayedColor); 439 } 440 } 441 442 locChildren = this._protectedChildren; 443 for(i =0, len = locChildren.length; i < len; i++) { 444 if (locChildren[i]) 445 locChildren[i].updateDisplayedColor(displayedColor); 446 } 447 }, 448 449 _disableCascadeOpacity: function () { 450 this._displayedOpacity = this._realOpacity; 451 452 var selChildren = this._children, i, item; 453 for (i = 0; i < selChildren.length; i++) { 454 item = selChildren[i]; 455 if (item) 456 item.updateDisplayedOpacity(255); 457 } 458 459 selChildren = this._protectedChildren; 460 for (i = 0; i < selChildren.length; i++) { 461 item = selChildren[i]; 462 if (item) 463 item.updateDisplayedOpacity(255); 464 } 465 }, 466 467 _disableCascadeColor: function(){ 468 var white = cc.color.WHITE; 469 var i, len, locChildren = this._children; 470 for(i = 0, len = locChildren.length; i < len; i++) 471 locChildren[i].updateDisplayedColor(white); 472 473 locChildren = this._protectedChildren; 474 for(i =0, len = locChildren.length; i < len; i++) 475 locChildren[i].setColor(white); 476 } 477 }); 478 479 if (cc._renderType === cc._RENDER_TYPE_CANVAS) { 480 cc.ProtectedNode.prototype.visit = cc.ProtectedNode.prototype._visitForCanvas; 481 }else{ 482 cc.ProtectedNode.prototype.visit = cc.ProtectedNode.prototype._visitForWebGL; 483 } 484 485 /** 486 * create a cc.ProtectedNode object; 487 * @deprecated since v3.0, please use new cc.ProtectedNode() instead. 488 * @return cc.ProtectedNode 489 */ 490 cc.ProtectedNode.create = function(){ 491 return new cc.ProtectedNode(); 492 };