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