1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 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 * Base class for ccs.Bone objects. 27 * @class 28 * @extends ccs.NodeRGBA 29 */ 30 ccs.Bone = ccs.NodeRGBA.extend(/** @lends ccs.Bone# */{ 31 _boneData:null, 32 _armature:null, 33 _childArmature:null, 34 _displayManager:null, 35 _ignoreMovementBoneData:false, 36 _tween:null, 37 _tweenData:null, 38 _name:"", 39 _childrenBone:null, 40 _parentBone:null, 41 _boneTransformDirty:false, 42 _worldTransform:null, 43 _blendFunc:0, 44 _blendDirty:false, 45 _worldInfo:null, 46 _armatureParentBone:null, 47 _dataVersion:0, 48 ctor:function () { 49 cc.NodeRGBA.prototype.ctor.call(this); 50 this._boneData = null; 51 this._armature = null; 52 this._childArmature = null; 53 this._displayManager = null; 54 this._ignoreMovementBoneData = false; 55 this._tween = null; 56 this._tweenData = null; 57 this._name = ""; 58 this._childrenBone = []; 59 this._parentBone = null; 60 this._boneTransformDirty = true; 61 this._worldTransform = cc.AffineTransformMake(1, 0, 0, 1, 0, 0); 62 this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); 63 this._blendDirty = false; 64 }, 65 66 /** 67 * release objects 68 */ 69 release:function () { 70 CC_SAFE_RELEASE(this._tweenData); 71 for (var i = 0; i < this._childrenBone.length; i++) { 72 CC_SAFE_RELEASE(this._childrenBone[i]); 73 } 74 this._childrenBone = []; 75 CC_SAFE_RELEASE(this._tween); 76 CC_SAFE_RELEASE(this._displayManager); 77 CC_SAFE_RELEASE(this._boneData); 78 CC_SAFE_RELEASE(this._childArmature); 79 }, 80 81 /** 82 * Initializes a CCBone with the specified name 83 * @param {String} name 84 * @return {Boolean} 85 */ 86 init:function (name) { 87 cc.NodeRGBA.prototype.init.call(this); 88 if (name) { 89 this._name = name; 90 } 91 this._tweenData = new ccs.FrameData(); 92 this._tween = new ccs.Tween(); 93 this._tween.init(this); 94 this._displayManager = new ccs.DisplayManager(); 95 this._displayManager.init(this); 96 this._worldInfo = new ccs.BaseData(); 97 this._boneData = new ccs.BaseData(); 98 return true; 99 }, 100 101 /** 102 * set the boneData 103 * @param {ccs.BoneData} boneData 104 */ 105 setBoneData:function (boneData) { 106 if (!boneData) { 107 cc.log("boneData must not be null"); 108 return; 109 } 110 this._boneData = boneData; 111 this._name = this._boneData.name; 112 this._zOrder = this._boneData.zOrder; 113 this._displayManager.initDisplayList(boneData); 114 }, 115 116 /** 117 * boneData getter 118 * @return {ccs.BoneData} 119 */ 120 getBoneData:function () { 121 return this._boneData; 122 }, 123 124 /** 125 * set the armature 126 * @param {ccs.Armature} armature 127 */ 128 setArmature:function (armature) { 129 this._armature = armature; 130 if(armature){ 131 this._tween.setAnimation(this._armature.getAnimation()); 132 this._dataVersion = this._armature.getArmatureData().dataVersion; 133 this._armatureParentBone = this._armature.getParentBone(); 134 }else{ 135 this._armatureParentBone = null; 136 } 137 }, 138 139 /** 140 * armature getter 141 * @return {ccs.Armature} 142 */ 143 getArmature:function () { 144 return this._armature; 145 }, 146 147 /** 148 * update worldTransform 149 * @param dt 150 */ 151 update:function (dt) { 152 var locParentBone = this._parentBone; 153 var locArmature = this._armature; 154 var locTweenData = this._tweenData; 155 var locWorldTransform = this._worldTransform; 156 var locWorldInfo = this._worldInfo; 157 var locArmatureParentBone = this._armatureParentBone; 158 159 if (locParentBone) { 160 this._boneTransformDirty = this._boneTransformDirty || locParentBone.isTransformDirty(); 161 } 162 if (locArmatureParentBone && !this._boneTransformDirty){ 163 this._boneTransformDirty = locArmatureParentBone.isTransformDirty(); 164 } 165 if (this._boneTransformDirty) { 166 if (this._dataVersion >= ccs.CONST_VERSION_COMBINED) { 167 var locBoneData = this._boneData; 168 locTweenData.x += locBoneData.x; 169 locTweenData.y += locBoneData.y; 170 locTweenData.skewX += locBoneData.skewX; 171 locTweenData.skewY += locBoneData.skewY; 172 locTweenData.scaleX += locBoneData.scaleX; 173 locTweenData.scaleY += locBoneData.scaleY; 174 175 locTweenData.scaleX -= 1; 176 locTweenData.scaleY -= 1; 177 } 178 179 locWorldInfo.x = locTweenData.x + this._position._x; 180 locWorldInfo.y = locTweenData.y + this._position._y; 181 locWorldInfo.scaleX = locTweenData.scaleX * this._scaleX; 182 locWorldInfo.scaleY = locTweenData.scaleY * this._scaleY; 183 locWorldInfo.skewX = locTweenData.skewX + this._skewX + this._rotationX; 184 locWorldInfo.skewY = locTweenData.skewY + this._skewY - this._rotationY; 185 186 if (this._parentBone) { 187 this.applyParentTransform(this._parentBone); 188 } 189 else { 190 if (locArmatureParentBone) { 191 this.applyParentTransform(locArmatureParentBone); 192 } 193 } 194 195 ccs.TransformHelp.nodeToMatrix(locWorldInfo, locWorldTransform); 196 197 if (locArmatureParentBone) { 198 this._worldTransform = cc.AffineTransformConcat(locWorldTransform, locArmature.nodeToParentTransform()); 199 } 200 } 201 ccs.DisplayFactory.updateDisplay(this, dt, this._boneTransformDirty || locArmature.getArmatureTransformDirty()); 202 203 var locChildrenBone = this._childrenBone; 204 for (var i = 0; i < locChildrenBone.length; i++) { 205 locChildrenBone[i].update(dt); 206 } 207 this._boneTransformDirty = false; 208 }, 209 210 applyParentTransform: function (parent) { 211 var locWorldInfo = this._worldInfo; 212 var locParentWorldTransform = parent._worldTransform; 213 var locParentWorldInfo = parent._worldInfo; 214 var x = locWorldInfo.x; 215 var y = locWorldInfo.y; 216 locWorldInfo.x = x * locParentWorldTransform.a + y * locParentWorldTransform.c + locParentWorldInfo.x; 217 locWorldInfo.y = x * locParentWorldTransform.b + y * locParentWorldTransform.d + locParentWorldInfo.y; 218 locWorldInfo.scaleX = locWorldInfo.scaleX * locParentWorldInfo.scaleX; 219 locWorldInfo.scaleY = locWorldInfo.scaleY * locParentWorldInfo.scaleY; 220 locWorldInfo.skewX = locWorldInfo.skewX + locParentWorldInfo.skewX; 221 locWorldInfo.skewY = locWorldInfo.skewY + locParentWorldInfo.skewY; 222 }, 223 224 /** 225 * Rewrite visit ,when node draw, g_NumberOfDraws is changeless 226 */ 227 visit:function (ctx) { 228 var node = this.getDisplayManager().getDisplayRenderNode(); 229 if (node) { 230 node.visit(ctx); 231 } 232 }, 233 234 /** 235 * update display color 236 * @param {cc.c3b} color 237 */ 238 updateDisplayedColor:function (color) { 239 this._realColor = cc.c3b(255,255,255); 240 cc.NodeRGBA.prototype.updateDisplayedColor.call(this, color); 241 this.updateColor(); 242 }, 243 244 /** 245 * update display opacity 246 * @param {Number} opacity 247 */ 248 updateDisplayedOpacity:function (opacity) { 249 this._realOpacity = 255; 250 cc.NodeRGBA.prototype.updateDisplayedOpacity.call(this, opacity); 251 this.updateColor(); 252 }, 253 254 /** 255 * set display color 256 * @param {cc.c3b} color 257 */ 258 setColor: function (color) { 259 cc.NodeRGBA.prototype.setColor.call(this, color); 260 this.updateColor(); 261 }, 262 263 /** 264 * set display opacity 265 * @param {Number} opacity 0-255 266 */ 267 setOpacity: function (opacity) { 268 cc.NodeRGBA.prototype.setOpacity.call(this, opacity); 269 this.updateColor(); 270 }, 271 272 /** 273 * update display color 274 */ 275 updateColor:function () { 276 var display = this._displayManager.getDisplayRenderNode(); 277 if (display && display.RGBAProtocol) { 278 var locDisplayedColor = this._displayedColor; 279 var locTweenData = this._tweenData; 280 var locOpacity = this._displayedOpacity * locTweenData.a / 255; 281 var locColor = cc.c3b(locDisplayedColor.r * locTweenData.r / 255, locDisplayedColor.g * locTweenData.g / 255, locDisplayedColor.b * locTweenData.b / 255); 282 display.setOpacity(locOpacity); 283 display.setColor(locColor); 284 } 285 }, 286 287 /** 288 * update display zOrder 289 */ 290 updateZOrder: function () { 291 if (this._armature.getArmatureData().dataVersion >= ccs.CONST_VERSION_COMBINED) { 292 var zorder = this._tweenData.zOrder + this._boneData.zOrder; 293 this.setZOrder(zorder); 294 } 295 else { 296 this.setZOrder(this._tweenData.zOrder); 297 } 298 }, 299 300 /** 301 * Add a child to this bone, and it will let this child call setParent(ccs.Bone) function to set self to it's parent 302 * @param {ccs.Bone} child 303 */ 304 addChildBone:function (child) { 305 if (!child) { 306 cc.log("Argument must be non-nil"); 307 return; 308 } 309 if (child._parentBone) { 310 cc.log("child already added. It can't be added again"); 311 return; 312 } 313 if (cc.ArrayGetIndexOfObject(this._childrenBone, child) < 0) { 314 this._childrenBone.push(child); 315 child.setParentBone(this); 316 } 317 }, 318 319 /** 320 * Removes a child bone 321 * @param {ccs.Bone} bone 322 * @param {Boolean} recursion 323 */ 324 removeChildBone:function (bone, recursion) { 325 for (var i = 0; i < this._childrenBone.length; i++) { 326 if (this._childrenBone[i] == bone) { 327 if (recursion) { 328 var ccbones = bone._childrenBone; 329 for (var j = 0; j < ccbones.length; j++) { 330 bone.removeChildBone(ccbones[j], recursion); 331 } 332 } 333 bone.setParentBone(null); 334 bone.getDisplayManager().setCurrentDecorativeDisplay(null); 335 cc.ArrayRemoveObject(this._childrenBone, bone); 336 } 337 } 338 }, 339 340 /** 341 * Remove itself from its parent CCBone. 342 * @param {Boolean} recursion 343 */ 344 removeFromParent:function (recursion) { 345 if (this._parentBone) { 346 this._parentBone.removeChildBone(this, recursion); 347 } 348 }, 349 350 /** 351 * Set parent bone. 352 * If _parent is NUll, then also remove this bone from armature. 353 * It will not set the CCArmature, if you want to add the bone to a CCArmature, you should use ccs.Armature.addBone(bone, parentName). 354 * @param {ccs.Bone} parent the parent bone. 355 */ 356 setParentBone:function (parent) { 357 this._parentBone = parent; 358 }, 359 360 /** 361 * parent bone getter 362 * @return {ccs.Bone} 363 */ 364 getParentBone:function () { 365 return this._parentBone; 366 }, 367 368 /** 369 * child armature setter 370 * @param {ccs.Armature} armature 371 */ 372 setChildArmature:function (armature) { 373 if (this._childArmature != armature) { 374 if (armature == null && this._childArmature) { 375 this._childArmature.setParentBone(null); 376 } 377 this._childArmature = armature; 378 } 379 }, 380 381 /** 382 * child armature getter 383 * @return {ccs.Armature} 384 */ 385 getChildArmature:function () { 386 return this._childArmature; 387 }, 388 389 /** 390 * child bone getter 391 * @return {Array} 392 */ 393 getChildrenBone:function () { 394 return this._childrenBone; 395 }, 396 397 /** 398 * tween getter 399 * @return {ccs.Tween} 400 */ 401 getTween:function () { 402 return this._tween; 403 }, 404 405 /** 406 * zOrder setter 407 * @param {Number} 408 */ 409 setZOrder:function (zOrder) { 410 if (this._zOrder != zOrder) 411 cc.Node.prototype.setZOrder.call(this, zOrder); 412 }, 413 414 /** 415 * transform dirty setter 416 * @param {Boolean} 417 */ 418 setTransformDirty:function (dirty) { 419 this._boneTransformDirty = dirty; 420 }, 421 422 /** 423 * transform dirty getter 424 * @return {Boolean} 425 */ 426 isTransformDirty:function () { 427 return this._boneTransformDirty; 428 }, 429 430 /** 431 * return world transform 432 * @return {{a:0.b:0,c:0,d:0,tx:0,ty:0}} 433 */ 434 nodeToArmatureTransform:function () { 435 return this._worldTransform; 436 }, 437 438 /** 439 * Returns the world affine transform matrix. The matrix is in Pixels. 440 * @returns {cc.AffineTransform} 441 */ 442 nodeToWorldTransform: function () { 443 return cc.AffineTransformConcat(this._worldTransform, this._armature.nodeToWorldTransform()); 444 }, 445 446 /** 447 * get render node 448 * @returns {cc.Node} 449 */ 450 getDisplayRenderNode: function () { 451 return this._displayManager.getDisplayRenderNode(); 452 }, 453 454 /** 455 * get render node type 456 * @returns {Number} 457 */ 458 getDisplayRenderNodeType: function () { 459 return this._displayManager.getDisplayRenderNodeType(); 460 }, 461 462 /** 463 * Add display and use _displayData init the display. 464 * If index already have a display, then replace it. 465 * If index is current display index, then also change display to _index 466 * @param {cc.Display} displayData it include the display information, like DisplayType. 467 * If you want to create a sprite display, then create a CCSpriteDisplayData param 468 *@param {Number} index the index of the display you want to replace or add to 469 * -1 : append display from back 470 */ 471 addDisplay:function (displayData, index) { 472 index = index || 0; 473 return this._displayManager.addDisplay(displayData, index); 474 }, 475 476 /** 477 * remove display 478 * @param {Number} index 479 */ 480 removeDisplay: function (index) { 481 this._displayManager.removeDisplay(index); 482 }, 483 484 addSkin:function (skin, index) { 485 index = index||0; 486 return this._displayManager.addSkin(skin, index); 487 }, 488 489 /** 490 * change display by index 491 * @param {Number} index 492 * @param {Boolean} force 493 */ 494 changeDisplayByIndex:function (index, force) { 495 cc.log("changeDisplayByIndex is deprecated. Use changeDisplayWithIndex instead."); 496 this.changeDisplayWithIndex(index, force); 497 }, 498 499 /** 500 * change display with index 501 * @param {Number} index 502 * @param {Boolean} force 503 */ 504 changeDisplayWithIndex:function (index, force) { 505 this._displayManager.changeDisplayWithIndex(index, force); 506 }, 507 508 /** 509 * change display with name 510 * @param {String} name 511 * @param {Boolean} force 512 */ 513 changeDisplayWithName:function (name, force) { 514 this._displayManager.changeDisplayWithName(name, force); 515 }, 516 517 /** 518 * get the collider body list in this bone. 519 * @returns {*} 520 */ 521 getColliderBodyList: function () { 522 var decoDisplay = this._displayManager.getCurrentDecorativeDisplay() 523 if (decoDisplay) { 524 var detector = decoDisplay.getColliderDetector() 525 if (detector) { 526 return detector.getColliderBodyList(); 527 } 528 } 529 return []; 530 }, 531 532 /** 533 * collider filter setter 534 * @param {cc.ColliderFilter} filter 535 */ 536 setColliderFilter: function (filter) { 537 var displayList = this._displayManager.getDecorativeDisplayList(); 538 for (var i = 0; i < displayList.length; i++) { 539 var locDecoDisplay = displayList[i]; 540 var locDetector = locDecoDisplay.getColliderDetector(); 541 if (locDetector) { 542 locDetector.setColliderFilter(filter); 543 } 544 } 545 546 }, 547 548 /** 549 * collider filter getter 550 * @returns {cc.ColliderFilter} 551 */ 552 getColliderFilter: function () { 553 var decoDisplay = this._displayManager.getCurrentDecorativeDisplay(); 554 if (decoDisplay) { 555 var detector = decoDisplay.getColliderDetector(); 556 if (detector) { 557 return detector.getColliderFilter(); 558 } 559 } 560 return null; 561 }, 562 563 /** 564 * displayManager setter 565 * @param {ccs.DisplayManager} 566 */ 567 setDisplayManager:function (displayManager) { 568 this._displayManager = displayManager; 569 }, 570 571 /** 572 * displayManager dirty getter 573 * @return {ccs.DisplayManager} 574 */ 575 getDisplayManager:function () { 576 return this._displayManager; 577 }, 578 579 /** 580 * When CCArmature play a animation, if there is not a CCMovementBoneData of this bone in this CCMovementData, this bone will hide. 581 * Set IgnoreMovementBoneData to true, then this bone will also show. 582 * @param {Boolean} bool 583 */ 584 setIgnoreMovementBoneData:function (bool) { 585 this._ignoreMovementBoneData = bool; 586 }, 587 588 /** 589 * ignoreMovementBoneData getter 590 * @return {Boolean} 591 */ 592 getIgnoreMovementBoneData:function () { 593 return this._ignoreMovementBoneData; 594 }, 595 596 /** 597 * tweenData getter 598 * @return {ccs.FrameData} 599 */ 600 getTweenData:function () { 601 return this._tweenData; 602 }, 603 604 /** 605 * name setter 606 * @param {String} name 607 */ 608 setName:function (name) { 609 this._name = name; 610 }, 611 612 /** 613 * name getter 614 * @return {String} 615 */ 616 getName:function () { 617 return this._name; 618 }, 619 620 /** 621 * BlendFunc setter 622 * @param {cc.BlendFunc} blendFunc 623 */ 624 setBlendFunc:function (blendFunc) { 625 if (this._blendFunc.src != blendFunc.src || this._blendFunc.dst != blendFunc.dst) { 626 this._blendFunc = blendFunc; 627 this._blendDirty = true; 628 } 629 }, 630 631 /** 632 * blendType getter 633 * @return {cc.BlendFunc} 634 */ 635 getBlendFunc:function () { 636 return this._blendFunc; 637 }, 638 639 setBlendDirty:function(dirty){ 640 this._blendDirty = dirty; 641 }, 642 643 isBlendDirty:function(){ 644 return this._blendDirty; 645 } 646 }); 647 648 /** 649 * allocates and initializes a bone. 650 * @constructs 651 * @return {ccs.Bone} 652 * @example 653 * // example 654 * var bone = ccs.Bone.create(); 655 */ 656 ccs.Bone.create = function (name) { 657 var bone = new ccs.Bone(); 658 if (bone && bone.init(name)) { 659 return bone; 660 } 661 return null; 662 };