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