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