1 /**************************************************************************** 2 Copyright (c) 2011-2012 cocos2d-x.org 3 Copyright (c) 2013-2014 Chukong Technologies Inc. 4 5 http://www.cocos2d-x.org 6 7 Permission is hereby granted, free of charge, to any person obtaining a copy 8 of this software and associated documentation files (the "Software"), to deal 9 in the Software without restriction, including without limitation the rights 10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 copies of the Software, and to permit persons to whom the Software is 12 furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included in 15 all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 THE SOFTWARE. 24 ****************************************************************************/ 25 26 /** 27 * The Bone of Armature, it has bone data, display manager and transform data for armature. 28 * @class 29 * @extends ccs.Node 30 * 31 * @property {ccs.BoneData} boneData - The bone data 32 * @property {ccs.Armature} armature - The armature 33 * @property {ccs.Bone} parentBone - The parent bone 34 * @property {ccs.Armature} childArmature - The child armature 35 * @property {Array} childrenBone - <@readonly> All children bones 36 * @property {ccs.Tween} tween - <@readonly> Tween 37 * @property {ccs.FrameData} tweenData - <@readonly> The tween data 38 * @property {ccs.ColliderFilter} colliderFilter - The collider filter 39 * @property {ccs.DisplayManager} displayManager - The displayManager 40 * @property {Boolean} ignoreMovementBoneData - Indicate whether force the bone to show When CCArmature play a animation and there isn't a CCMovementBoneData of this bone in this CCMovementData. 41 * @property {String} name - The name of the bone 42 * @property {Boolean} blendDirty - Indicate whether the blend is dirty 43 * 44 */ 45 ccs.Bone = ccs.Node.extend(/** @lends ccs.Bone# */{ 46 _boneData: null, 47 _armature: null, 48 _childArmature: null, 49 _displayManager: null, 50 ignoreMovementBoneData: false, 51 _tween: null, 52 _tweenData: null, 53 _parentBone: null, 54 _boneTransformDirty: false, 55 _worldTransform: null, 56 _blendFunc: null, 57 blendDirty: false, 58 _worldInfo: null, 59 _armatureParentBone: null, 60 _dataVersion: 0, 61 _className: "Bone", 62 63 /** 64 * Construction of ccs.Bone. 65 */ 66 ctor: function () { 67 cc.Node.prototype.ctor.call(this); 68 this._tweenData = null; 69 this._parentBone = null; 70 this._armature = null; 71 this._childArmature = null; 72 this._boneData = null; 73 this._tween = null; 74 this._displayManager = null; 75 this.ignoreMovementBoneData = false; 76 77 this._worldTransform = cc.affineTransformMake(1, 0, 0, 1, 0, 0); 78 this._boneTransformDirty = true; 79 this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); 80 this.blendDirty = false; 81 this._worldInfo = null; 82 83 this._armatureParentBone = null; 84 this._dataVersion = 0; 85 }, 86 87 /** 88 * Initializes a ccs.Bone with the specified name 89 * @param {String} name bone name 90 * @return {Boolean} 91 */ 92 init: function (name) { 93 // cc.Node.prototype.init.call(this); 94 if (name) 95 this._name = name; 96 this._tweenData = new ccs.FrameData(); 97 98 this._tween = new ccs.Tween(); 99 this._tween.init(this); 100 101 this._displayManager = new ccs.DisplayManager(); 102 this._displayManager.init(this); 103 104 this._worldInfo = new ccs.BaseData(); 105 this._boneData = new ccs.BaseData(); 106 107 return true; 108 }, 109 110 /** 111 * Sets the boneData to ccs.Bone. 112 * @param {ccs.BoneData} boneData 113 */ 114 setBoneData: function (boneData) { 115 cc.assert(boneData, "_boneData must not be null"); 116 117 if(this._boneData != boneData) 118 this._boneData = boneData; 119 120 this.setName(this._boneData.name); 121 this._localZOrder = this._boneData.zOrder; 122 this._displayManager.initDisplayList(boneData); 123 }, 124 125 /** 126 * Returns boneData of ccs.Bone. 127 * @return {ccs.BoneData} 128 */ 129 getBoneData: function () { 130 return this._boneData; 131 }, 132 133 /** 134 * Sets the armature reference to ccs.Bone. 135 * @param {ccs.Armature} armature 136 */ 137 setArmature: function (armature) { 138 this._armature = armature; 139 if (armature) { 140 this._tween.setAnimation(this._armature.getAnimation()); 141 this._dataVersion = this._armature.getArmatureData().dataVersion; 142 this._armatureParentBone = this._armature.getParentBone(); 143 } else 144 this._armatureParentBone = null; 145 }, 146 147 /** 148 * Returns the armature reference of ccs.Bone. 149 * @return {ccs.Armature} 150 */ 151 getArmature: function () { 152 return this._armature; 153 }, 154 155 /** 156 * Updates worldTransform by tween data and updates display state 157 * @param {Number} delta 158 */ 159 update: function (delta) { 160 if (this._parentBone) 161 this._boneTransformDirty = this._boneTransformDirty || this._parentBone.isTransformDirty(); 162 163 if (this._armatureParentBone && !this._boneTransformDirty) 164 this._boneTransformDirty = this._armatureParentBone.isTransformDirty(); 165 166 if (this._boneTransformDirty){ 167 var locTweenData = this._tweenData; 168 if (this._dataVersion >= ccs.CONST_VERSION_COMBINED){ 169 ccs.TransformHelp.nodeConcat(locTweenData, this._boneData); 170 locTweenData.scaleX -= 1; 171 locTweenData.scaleY -= 1; 172 } 173 174 var locWorldInfo = this._worldInfo; 175 locWorldInfo.copy(locTweenData); 176 locWorldInfo.x = locTweenData.x + this._position.x; 177 locWorldInfo.y = locTweenData.y + this._position.y; 178 locWorldInfo.scaleX = locTweenData.scaleX * this._scaleX; 179 locWorldInfo.scaleY = locTweenData.scaleY * this._scaleY; 180 locWorldInfo.skewX = locTweenData.skewX + this._skewX + this._rotationX; 181 locWorldInfo.skewY = locTweenData.skewY + this._skewY - this._rotationY; 182 183 if(this._parentBone) 184 this._applyParentTransform(this._parentBone); 185 else { 186 if (this._armatureParentBone) 187 this._applyParentTransform(this._armatureParentBone); 188 } 189 190 ccs.TransformHelp.nodeToMatrix(locWorldInfo, this._worldTransform); 191 if (this._armatureParentBone) 192 this._worldTransform = cc.affineTransformConcat(this._worldTransform, this._armature.getNodeToParentTransform()); //TODO TransformConcat 193 } 194 195 ccs.displayFactory.updateDisplay(this, delta, this._boneTransformDirty || this._armature.getArmatureTransformDirty()); 196 for(var i=0; i<this._children.length; i++) { 197 var childBone = this._children[i]; 198 childBone.update(delta); 199 } 200 this._boneTransformDirty = false; 201 }, 202 203 _applyParentTransform: function (parent) { 204 var locWorldInfo = this._worldInfo; 205 var locParentWorldTransform = parent._worldTransform; 206 var locParentWorldInfo = parent._worldInfo; 207 var x = locWorldInfo.x; 208 var y = locWorldInfo.y; 209 locWorldInfo.x = x * locParentWorldTransform.a + y * locParentWorldTransform.c + locParentWorldInfo.x; 210 locWorldInfo.y = x * locParentWorldTransform.b + y * locParentWorldTransform.d + locParentWorldInfo.y; 211 locWorldInfo.scaleX = locWorldInfo.scaleX * locParentWorldInfo.scaleX; 212 locWorldInfo.scaleY = locWorldInfo.scaleY * locParentWorldInfo.scaleY; 213 locWorldInfo.skewX = locWorldInfo.skewX + locParentWorldInfo.skewX; 214 locWorldInfo.skewY = locWorldInfo.skewY + locParentWorldInfo.skewY; 215 }, 216 217 /** 218 * Sets BlendFunc to ccs.Bone. 219 * @param {cc.BlendFunc|Number} blendFunc blendFunc or src of blendFunc 220 * @param {Number} [dst] dst of blendFunc 221 */ 222 setBlendFunc: function (blendFunc, dst) { 223 var locBlendFunc = this._blendFunc, srcValue, dstValue; 224 if(dst === undefined){ 225 srcValue = blendFunc.src; 226 dstValue = blendFunc.dst; 227 } else { 228 srcValue = blendFunc; 229 dstValue = dst; 230 } 231 if (locBlendFunc.src != srcValue || locBlendFunc.dst != dstValue) { 232 locBlendFunc.src = srcValue; 233 locBlendFunc.dst = dstValue; 234 this.blendDirty = true; 235 } 236 }, 237 238 /** 239 * Updates display color 240 * @override 241 * @param {cc.Color} color 242 */ 243 updateDisplayedColor: function (color) { 244 this._realColor = cc.color(255, 255, 255); 245 cc.Node.prototype.updateDisplayedColor.call(this, color); 246 this.updateColor(); 247 }, 248 249 /** 250 * Updates display opacity 251 * @param {Number} opacity 252 */ 253 updateDisplayedOpacity: function (opacity) { 254 this._realOpacity = 255; 255 cc.Node.prototype.updateDisplayedOpacity.call(this, opacity); 256 this.updateColor(); 257 }, 258 259 /** 260 * Updates display color 261 */ 262 updateColor: function () { 263 var display = this._displayManager.getDisplayRenderNode(); 264 if (display != null) { 265 display.setColor( 266 cc.color( 267 this._displayedColor.r * this._tweenData.r / 255, 268 this._displayedColor.g * this._tweenData.g / 255, 269 this._displayedColor.b * this._tweenData.b / 255)); 270 display.setOpacity(this._displayedOpacity * this._tweenData.a / 255); 271 } 272 }, 273 274 /** 275 * Updates display zOrder 276 */ 277 updateZOrder: function () { 278 if (this._armature.getArmatureData().dataVersion >= ccs.CONST_VERSION_COMBINED) { 279 var zorder = this._tweenData.zOrder + this._boneData.zOrder; 280 this.setLocalZOrder(zorder); 281 } else { 282 this.setLocalZOrder(this._tweenData.zOrder); 283 } 284 }, 285 286 /** 287 * Adds a child to this bone, and it will let this child call setParent(ccs.Bone) function to set self to it's parent 288 * @param {ccs.Bone} child 289 */ 290 addChildBone: function (child) { 291 cc.assert(child, "Argument must be non-nil"); 292 cc.assert(!child.parentBone, "child already added. It can't be added again"); 293 294 if (this._children.indexOf(child) < 0) { 295 this._children.push(child); 296 child.setParentBone(this); 297 } 298 }, 299 300 /** 301 * Removes a child bone 302 * @param {ccs.Bone} bone 303 * @param {Boolean} recursion 304 */ 305 removeChildBone: function (bone, recursion) { 306 if (this._children.length > 0 && this._children.getIndex(bone) != -1 ) { 307 if(recursion) { 308 var ccbones = bone._children; 309 for(var i=0; i<ccbones.length; i++){ 310 var ccBone = ccbones[i]; 311 bone.removeChildBone(ccBone, recursion); 312 } 313 } 314 315 bone.setParentBone(null); 316 bone.getDisplayManager().setCurrentDecorativeDisplay(null); 317 cc.arrayRemoveObject(this._children, bone); 318 } 319 }, 320 321 /** 322 * Removes itself from its parent ccs.Bone. 323 * @param {Boolean} recursion 324 */ 325 removeFromParent: function (recursion) { 326 if (this._parentBone) 327 this._parentBone.removeChildBone(this, recursion); 328 }, 329 330 /** 331 * Sets parent bone to ccs.Bone. 332 * If _parent is NUll, then also remove this bone from armature. 333 * It will not set the ccs.Armature, if you want to add the bone to a ccs.Armature, you should use ccs.Armature.addBone(bone, parentName). 334 * @param {ccs.Bone} parent the parent bone. 335 */ 336 setParentBone: function (parent) { 337 this._parentBone = parent; 338 }, 339 340 /** 341 * Returns the parent bone of ccs.Bone. 342 * @returns {ccs.Bone} 343 */ 344 getParentBone: function(){ 345 return this._parentBone; 346 }, 347 348 /** 349 * Sets ccs.Bone's child armature 350 * @param {ccs.Armature} armature 351 */ 352 setChildArmature: function (armature) { 353 if (this._childArmature != armature) { 354 if (armature == null && this._childArmature) 355 this._childArmature.setParentBone(null); 356 this._childArmature = armature; 357 } 358 }, 359 360 /** 361 * Returns ccs.Bone's child armature. 362 * @return {ccs.Armature} 363 */ 364 getChildArmature: function () { 365 return this._childArmature; 366 }, 367 368 /** 369 * Return the tween of ccs.Bone 370 * @return {ccs.Tween} 371 */ 372 getTween: function () { 373 return this._tween; 374 }, 375 376 /** 377 * Sets the local zOrder to ccs.Bone. 378 * @param {Number} zOrder 379 */ 380 setLocalZOrder: function (zOrder) { 381 if (this._localZOrder != zOrder) 382 cc.Node.prototype.setLocalZOrder.call(this, zOrder); 383 }, 384 385 /** 386 * Return the worldTransform of ccs.Bone. 387 * @returns {cc.AffineTransform} 388 */ 389 getNodeToArmatureTransform: function(){ 390 return this._worldTransform; 391 }, 392 393 /** 394 * Returns the world transform of ccs.Bone. 395 * @override 396 * @returns {cc.AffineTransform} 397 */ 398 getNodeToWorldTransform: function(){ 399 return cc.affineTransformConcat(this._worldTransform, this._armature.getNodeToWorldTransform()); 400 }, 401 402 /** 403 * Returns the display render node. 404 * @returns {cc.Node} 405 */ 406 getDisplayRenderNode: function () { 407 return this._displayManager.getDisplayRenderNode(); 408 }, 409 410 /** 411 * Returns the type of display render node 412 * @returns {Number} 413 */ 414 getDisplayRenderNodeType: function () { 415 return this._displayManager.getDisplayRenderNodeType(); 416 }, 417 418 /** 419 * Add display and use _displayData init the display. 420 * If index already have a display, then replace it. 421 * If index is current display index, then also change display to _index 422 * @param {ccs.DisplayData} displayData it include the display information, like DisplayType. 423 * If you want to create a sprite display, then create a CCSpriteDisplayData param 424 *@param {Number} index the index of the display you want to replace or add to 425 * -1 : append display from back 426 */ 427 addDisplay: function (displayData, index) { 428 index = index || 0; 429 return this._displayManager.addDisplay(displayData, index); 430 }, 431 432 /** 433 * Removes display by index. 434 * @param {Number} index display renderer's index 435 */ 436 removeDisplay: function (index) { 437 this._displayManager.removeDisplay(index); 438 }, 439 440 /** 441 * Changes display by index 442 * @deprecated since v3.0, please use changeDisplayWithIndex instead. 443 * @param {Number} index 444 * @param {Boolean} force 445 */ 446 changeDisplayByIndex: function (index, force) { 447 cc.log("changeDisplayByIndex is deprecated. Use changeDisplayWithIndex instead."); 448 this.changeDisplayWithIndex(index, force); 449 }, 450 451 /** 452 * Changes display by name 453 * @deprecated since v3.0, please use changeDisplayWithName instead. 454 * @param {String} name 455 * @param {Boolean} force 456 */ 457 changeDisplayByName: function(name, force){ 458 cc.log("changeDisplayByName is deprecated. Use changeDisplayWithName instead."); 459 this.changeDisplayWithName(name, force); 460 }, 461 462 /** 463 * Changes display with index 464 * @param {Number} index 465 * @param {Boolean} force 466 */ 467 changeDisplayWithIndex: function (index, force) { 468 this._displayManager.changeDisplayWithIndex(index, force); 469 }, 470 471 /** 472 * Changes display with name 473 * @param {String} name 474 * @param {Boolean} force 475 */ 476 changeDisplayWithName: function (name, force) { 477 this._displayManager.changeDisplayWithName(name, force); 478 }, 479 480 /** 481 * Returns the collide detector of ccs.Bone. 482 * @returns {*} 483 */ 484 getColliderDetector: function(){ 485 var decoDisplay = this._displayManager.getCurrentDecorativeDisplay(); 486 if (decoDisplay){ 487 var detector = decoDisplay.getColliderDetector(); 488 if (detector) 489 return detector; 490 } 491 return null; 492 }, 493 494 /** 495 * Sets collider filter to ccs.Bone. 496 * @param {ccs.ColliderFilter} filter 497 */ 498 setColliderFilter: function (filter) { 499 var displayList = this._displayManager.getDecorativeDisplayList(); 500 for (var i = 0; i < displayList.length; i++) { 501 var locDecoDisplay = displayList[i]; 502 var locDetector = locDecoDisplay.getColliderDetector(); 503 if (locDetector) 504 locDetector.setColliderFilter(filter); 505 } 506 }, 507 508 /** 509 * Returns collider filter of ccs.Bone. 510 * @returns {cc.ColliderFilter} 511 */ 512 getColliderFilter: function () { 513 var decoDisplay = this.displayManager.getCurrentDecorativeDisplay(); 514 if (decoDisplay) { 515 var detector = decoDisplay.getColliderDetector(); 516 if (detector) 517 return detector.getColliderFilter(); 518 } 519 return null; 520 }, 521 522 /** 523 * Sets ccs.Bone's transform dirty flag. 524 * @param {Boolean} dirty 525 */ 526 setTransformDirty: function (dirty) { 527 this._boneTransformDirty = dirty; 528 }, 529 530 /** 531 * Returns ccs.Bone's transform dirty flag whether is dirty. 532 * @return {Boolean} 533 */ 534 isTransformDirty: function () { 535 return this._boneTransformDirty; 536 }, 537 538 /** 539 * displayManager dirty getter 540 * @return {ccs.DisplayManager} 541 */ 542 getDisplayManager: function () { 543 return this._displayManager; 544 }, 545 546 /** 547 * When CCArmature play a animation, if there is not a CCMovementBoneData of this bone in this CCMovementData, this bone will hide. <br/> 548 * Set IgnoreMovementBoneData to true, then this bone will also show. 549 * @param {Boolean} bool 550 */ 551 setIgnoreMovementBoneData: function (bool) { 552 this._ignoreMovementBoneData = bool; 553 }, 554 555 /** 556 * Returns whether is ignore movement bone data. 557 * @returns {Boolean} 558 */ 559 isIgnoreMovementBoneData: function(){ 560 return this._ignoreMovementBoneData; 561 }, 562 563 /** 564 * Returns the blendFunc of ccs.Bone. 565 * @return {cc.BlendFunc} 566 */ 567 getBlendFunc: function () { 568 return this._blendFunc; 569 }, 570 571 /** 572 * Sets blend dirty flag 573 * @param {Boolean} dirty 574 */ 575 setBlendDirty: function (dirty) { 576 this._blendDirty = dirty; 577 }, 578 579 /** 580 * Returns the blend dirty flag whether is dirty. 581 * @returns {Boolean|*|ccs.Bone._blendDirty} 582 */ 583 isBlendDirty: function () { 584 return this._blendDirty; 585 }, 586 587 /** 588 * Returns the tweenData of ccs.Bone. 589 * @return {ccs.FrameData} 590 */ 591 getTweenData: function () { 592 return this._tweenData; 593 }, 594 595 /** 596 * Returns the world information of ccs.Bone. 597 * @returns {ccs.BaseData} 598 */ 599 getWorldInfo: function(){ 600 return this._worldInfo; 601 }, 602 603 /** 604 * Returns the children of ccs.Bone 605 * @return {Array} 606 * @deprecated since v3.0, please use getChildren instead. 607 */ 608 getChildrenBone: function () { 609 return this._children; 610 }, 611 612 /** 613 * Returns the worldTransform of ccs.Bone. 614 * @return {cc.AffineTransform} 615 * @deprecated since v3.0, please use getNodeToArmatureTransform instead. 616 */ 617 nodeToArmatureTransform: function () { 618 return this.getNodeToArmatureTransform(); 619 }, 620 621 /** 622 * @deprecated 623 * Returns the world affine transform matrix. The matrix is in Pixels. 624 * @returns {cc.AffineTransform} 625 */ 626 nodeToWorldTransform: function () { 627 return this.getNodeToWorldTransform(); 628 }, 629 630 /** 631 * Returns the collider body list in this bone. 632 * @returns {Array|null} 633 * @deprecated since v3.0, please use getColliderDetector to get a delector, and calls its getColliderBodyList instead. 634 */ 635 getColliderBodyList: function () { 636 var detector = this.getColliderDetector(); 637 if(detector) 638 return detector.getColliderBodyList(); 639 return null; 640 }, 641 642 /** 643 * Returns whether is ignore movement bone data. 644 * @return {Boolean} 645 * @deprecated since v3.0, please isIgnoreMovementBoneData instead. 646 */ 647 getIgnoreMovementBoneData: function () { 648 return this.isIgnoreMovementBoneData(); 649 } 650 }); 651 652 var _p = ccs.Bone.prototype; 653 654 // Extended properties 655 /** @expose */ 656 _p.boneData; 657 cc.defineGetterSetter(_p, "boneData", _p.getBoneData, _p.setBoneData); 658 /** @expose */ 659 _p.armature; 660 cc.defineGetterSetter(_p, "armature", _p.getArmature, _p.setArmature); 661 /** @expose */ 662 _p.childArmature; 663 cc.defineGetterSetter(_p, "childArmature", _p.getChildArmature, _p.setChildArmature); 664 /** @expose */ 665 _p.childrenBone; 666 cc.defineGetterSetter(_p, "childrenBone", _p.getChildrenBone); 667 /** @expose */ 668 _p.tween; 669 cc.defineGetterSetter(_p, "tween", _p.getTween); 670 /** @expose */ 671 _p.tweenData; 672 cc.defineGetterSetter(_p, "tweenData", _p.getTweenData); 673 /** @expose */ 674 _p.colliderFilter; 675 cc.defineGetterSetter(_p, "colliderFilter", _p.getColliderFilter, _p.setColliderFilter); 676 677 _p = null; 678 679 /** 680 * Allocates and initializes a bone. 681 * @return {ccs.Bone} 682 * @example 683 * // example 684 * var bone = ccs.Bone.create(); 685 */ 686 ccs.Bone.create = function (name) { 687 var bone = new ccs.Bone(); 688 if (bone && bone.init(name)) 689 return bone; 690 return null; 691 };