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 * movement event type 28 * @type {Object} 29 */ 30 ccs.MovementEventType = { 31 start: 0, 32 complete: 1, 33 loopComplete: 2 34 }; 35 /** 36 * Base class for cc.MovementEvent objects. 37 * @class 38 * @extends ccs.Class 39 */ 40 ccs.AnimationEvent = ccs.Class.extend(/** @lends ccs.AnimationEvent# */{ 41 _arguments: null, 42 _callFunc: null, 43 _selectorTarget: null, 44 ctor: function (target, callFunc, data) { 45 this._data = data; 46 this._callFunc = callFunc; 47 this._selectorTarget = target; 48 }, 49 call: function () { 50 if (this._callFunc) { 51 this._callFunc.apply(this._selectorTarget, this._arguments); 52 } 53 }, 54 setArguments: function (args) { 55 this._arguments = args; 56 } 57 }); 58 /** 59 * movement event 60 * @constructor 61 */ 62 ccs.MovementEvent = function () { 63 this.armature = null; 64 this.movementType = ""; 65 this.movementID = ""; 66 }; 67 /** 68 * frame event 69 * @constructor 70 */ 71 ccs.FrameEvent = function () { 72 this.bone = null; 73 this.frameEventName = ""; 74 this.originFrameIndex = 0; 75 this.currentFrameIndex = 0; 76 }; 77 /** 78 * Base class for ccs.ArmatureAnimation objects. 79 * @class 80 * @extends ccs.ProcessBase 81 * 82 * @property {ccs.AnimationData} animationData - Animation data 83 * @property {Object} userObject - User custom object 84 * @property {Boolean} ignoreFrameEvent - Indicate whether the frame event is ignored 85 * @property {Number} speedScale - Animation play speed scale 86 * @property {Number} animationScale - Animation play speed scale 87 * 88 */ 89 ccs.ArmatureAnimation = ccs.ProcessBase.extend(/** @lends ccs.ArmatureAnimation# */{ 90 animationData: null, 91 _movementData: null, 92 _armature: null, 93 _movementID: "", 94 _prevFrameIndex: 0, 95 _toIndex: 0, 96 _tweenList: null, 97 _frameEvent: null, 98 _movementEvent: null, 99 _speedScale: 1, 100 ignoreFrameEvent: false, 101 _frameEventQueue: null, 102 _movementEventQueue: null, 103 userObject: null, 104 _movementList: null, 105 _onMovementList: false, 106 _movementListLoop: false, 107 _movementIndex: 0, 108 ctor: function () { 109 ccs.ProcessBase.prototype.ctor.call(this); 110 this.animationData = null; 111 this._movementData = null; 112 this._movementID = ""; 113 this._armature = null; 114 this._prevFrameIndex = 0; 115 this._toIndex = 0; 116 this._tweenList = []; 117 this._frameEvent = null; 118 this._movementEvent = null; 119 this._speedScale = 1; 120 this.ignoreFrameEvent = false; 121 this._frameEventQueue = []; 122 this._movementEventQueue = []; 123 this.userObject = null; 124 this._movementList = []; 125 this._onMovementList = false; 126 this._movementListLoop = false; 127 this._movementIndex = 0; 128 }, 129 130 /** 131 * init with a CCArmature 132 * @param {ccs.Armature} armature 133 * @return {Boolean} 134 */ 135 init: function (armature) { 136 this._armature = armature; 137 this._tweenList = []; 138 return true; 139 }, 140 pause: function () { 141 for (var i = 0; i < this._tweenList.length; i++) { 142 this._tweenList[i].pause(); 143 } 144 ccs.ProcessBase.prototype.pause.call(this); 145 }, 146 resume: function () { 147 for (var i = 0; i < this._tweenList.length; i++) { 148 this._tweenList[i].resume(); 149 } 150 ccs.ProcessBase.prototype.resume.call(this); 151 }, 152 153 stop: function () { 154 for (var i = 0; i < this._tweenList.length; i++) { 155 this._tweenList[i].stop(); 156 } 157 this._tweenList = []; 158 ccs.ProcessBase.prototype.stop.call(this); 159 }, 160 161 /** 162 * scale animation play speed 163 * @param {Number} speedScale 164 */ 165 setSpeedScale: function (speedScale) { 166 if (speedScale == this._speedScale) { 167 return; 168 } 169 this._speedScale = speedScale; 170 this._processScale = !this._movementData ? this._speedScale : this._speedScale * this._movementData.scale; 171 var dict = this._armature.getBoneDic(); 172 for (var key in dict) { 173 var bone = dict[key]; 174 bone.getTween().setProcessScale(this._processScale); 175 if (bone.getChildArmature()) { 176 bone.getChildArmature().getAnimation().setProcessScale(this._processScale); 177 } 178 } 179 }, 180 181 getSpeedScale: function () { 182 return this._speedScale; 183 }, 184 185 getAnimationScale: function () { 186 return this.getSpeedScale(); 187 }, 188 setAnimationScale: function (animationScale) { 189 return this.setSpeedScale(animationScale); 190 }, 191 192 /** 193 * play animation by animation name. 194 * @param {String} animationName The animation name you want to play 195 * @param {Number} [durationTo=-1] 196 * he frames between two animation changing-over.It's meaning is changing to this animation need how many frames 197 * -1 : use the value from CCMovementData get from flash design panel 198 * @param {Number} [loop=-1] 199 * Whether the animation is loop. 200 * loop < 0 : use the value from CCMovementData get from flash design panel 201 * loop = 0 : this animation is not loop 202 * loop > 0 : this animation is loop 203 * @example 204 * // example 205 * armature.getAnimation().play("run",-1,1);//loop play 206 * armature.getAnimation().play("run",-1,0);//not loop play 207 */ 208 play: function (animationName, durationTo, loop) { 209 if (this.animationData == null) { 210 cc.log("this.animationData can not be null"); 211 return; 212 } 213 this._movementData = this.animationData.getMovement(animationName); 214 if (this._movementData == null) { 215 cc.log("this._movementData can not be null"); 216 return; 217 } 218 if (durationTo === undefined) { 219 durationTo = -1; 220 } 221 222 if (loop === undefined) { 223 loop = -1; 224 } 225 226 var locMovementData = this._movementData; 227 //Get key frame count 228 this._rawDuration = locMovementData.duration; 229 this._movementID = animationName; 230 this._processScale = this._speedScale * locMovementData.scale; 231 //Further processing parameters 232 durationTo = (durationTo == -1) ? locMovementData.durationTo : durationTo; 233 var durationTween = locMovementData.durationTween; 234 durationTween = (durationTween == 0) ? this._rawDuration : durationTween;//todo 235 var tweenEasing = locMovementData.tweenEasing; 236 237 if (loop < 0) { 238 loop = locMovementData.loop; 239 } else { 240 loop = Boolean(loop); 241 } 242 243 this._onMovementList = false; 244 ccs.ProcessBase.prototype.play.call(this, durationTo, tweenEasing); 245 246 if (this._rawDuration == 0) { 247 this._loopType = ccs.ANIMATION_TYPE_SINGLE_FRAME; 248 } 249 else { 250 if (loop) { 251 this._loopType = ccs.ANIMATION_TYPE_TO_LOOP_FRONT; 252 } 253 else { 254 this._loopType = ccs.ANIMATION_TYPE_NO_LOOP; 255 } 256 this._durationTween = durationTween; 257 } 258 259 this._tweenList = []; 260 261 var movementBoneData; 262 var dict = this._armature.getBoneDic(); 263 for (var key in dict) { 264 var bone = dict[key]; 265 movementBoneData = locMovementData.getMovementBoneData(bone.getName()); 266 var tween = bone.getTween(); 267 if (movementBoneData && movementBoneData.frameList.length > 0) { 268 this._tweenList.push(tween); 269 movementBoneData.duration = locMovementData.duration; 270 tween.play(movementBoneData, durationTo, durationTween, loop, tweenEasing); 271 272 tween.setProcessScale(this._processScale); 273 if (bone.getChildArmature()) { 274 bone.getChildArmature().getAnimation().setProcessScale(this._processScale); 275 } 276 } else { 277 if (!bone.getIgnoreMovementBoneData()) { 278 bone.getDisplayManager().changeDisplayWithIndex(-1, false); 279 tween.stop(); 280 } 281 } 282 } 283 this._armature.update(0); 284 }, 285 286 /** 287 * play with names 288 * @param {Array} movementNames 289 * @param {Number} durationTo 290 * @param {Boolean} loop 291 */ 292 playWithNames: function (movementNames, durationTo, loop) { 293 this._movementList = []; 294 this._movementListLoop = loop; 295 this._onMovementList = true; 296 this._movementIndex = 0; 297 298 for (var i = 0; i < movementNames.length; i++) { 299 this._movementList.push({name: movementNames[i], durationTo: durationTo}); 300 } 301 302 this.updateMovementList(); 303 }, 304 305 updateMovementList: function () { 306 if (this._onMovementList) { 307 if (this._movementListLoop) { 308 var movementObj = this._movementList[this._movementIndex]; 309 this.play(movementObj.name, movementObj.durationTo, -1, 0); 310 this._movementIndex++; 311 if (this._movementIndex >= this._movementList.length) { 312 this._movementIndex = 0; 313 } 314 } 315 else { 316 if (this._movementIndex < this._movementList.length) { 317 var movementObj = this._movementList[this._movementIndex]; 318 this.play(movementObj.name, movementObj.durationTo, -1, 0); 319 this._movementIndex++; 320 } 321 else { 322 this._onMovementList = false; 323 } 324 } 325 this._onMovementList = true; 326 } 327 }, 328 329 /** 330 * Go to specified frame and play current movement. 331 * You need first switch to the movement you want to play, then call this function. 332 * 333 * example : playByIndex(0); 334 * gotoAndPlay(0); 335 * playByIndex(1); 336 * gotoAndPlay(0); 337 * gotoAndPlay(15); 338 * @param {Number} frameIndex 339 */ 340 gotoAndPlay: function (frameIndex) { 341 if (!this._movementData || frameIndex < 0 || frameIndex >= this._movementData.duration) { 342 cc.log("Please ensure you have played a movement, and the frameIndex is in the range."); 343 return; 344 } 345 346 var ignoreFrameEvent = this.ignoreFrameEvent; 347 this.ignoreFrameEvent = true; 348 this._isPlaying = true; 349 this._isComplete = this._isPause = false; 350 351 ccs.ProcessBase.prototype.gotoFrame.call(this, frameIndex); 352 this._currentPercent = this._curFrameIndex / (this._movementData.duration - 1); 353 this._currentFrame = this._nextFrameIndex * this._currentPercent; 354 355 for (var i = 0; i < this._tweenList.length; i++) { 356 var tween = this._tweenList[i]; 357 tween.gotoAndPlay(frameIndex); 358 } 359 this._armature.update(0); 360 this.ignoreFrameEvent = ignoreFrameEvent; 361 }, 362 363 /** 364 * Go to specified frame and pause current movement. 365 * @param {Number} frameIndex 366 */ 367 gotoAndPause: function (frameIndex) { 368 this.gotoAndPlay(frameIndex); 369 this.pause(); 370 }, 371 372 /** 373 * Play animation with index, the other param is the same to play. 374 * @param {Number||Array} animationIndex 375 * @param {Number} durationTo 376 * @param {Number} durationTween 377 * @param {Number} loop 378 * @param {Number} tweenEasing 379 */ 380 playWithIndex: function (animationIndex, durationTo, durationTween, loop, tweenEasing) { 381 if (typeof durationTo == "undefined") { 382 durationTo = -1; 383 } 384 if (typeof loop == "undefined") { 385 loop = -1; 386 } 387 var moveNames = this.animationData.movementNames; 388 if (animationIndex < -1 || animationIndex >= moveNames.length) { 389 return; 390 } 391 var animationName = moveNames[animationIndex]; 392 this.play(animationName, durationTo, -1, loop, 0); 393 }, 394 395 /** 396 * Play animation with index, the o ther param is the same to play. 397 * @param {Number} animationIndex 398 * @param {Number} durationTo 399 * @param {Number} durationTween 400 * @param {Number} loop 401 * @param {Number} tweenEasing 402 */ 403 playByIndex: function (animationIndex, durationTo, durationTween, loop, tweenEasing) { 404 cc.log("playByIndex is deprecated. Use playWithIndex instead."); 405 this.playWithIndex(animationIndex, durationTo, durationTween, loop, tweenEasing); 406 }, 407 408 /** 409 * play by indexes 410 * @param movementIndexes 411 * @param {Number} durationTo 412 * @param {Boolean} loop 413 */ 414 playWithIndexes: function (movementIndexes, durationTo, loop) { 415 this._movementList = []; 416 this._movementListLoop = loop; 417 this._onMovementList = true; 418 this._movementIndex = 0; 419 420 var movName = this.animationData.movementNames; 421 422 for (var i = 0; i < movementIndexes.length; i++) { 423 var name = movName[movementIndexes[i]]; 424 this._movementList.push({name: name, durationTo: durationTo}); 425 } 426 427 this.updateMovementList(); 428 }, 429 430 /** 431 * get movement count 432 * @return {Number} 433 */ 434 getMovementCount: function () { 435 return this.animationData.getMovementCount(); 436 }, 437 438 update: function (dt) { 439 if (ccs.ProcessBase.prototype.update.call(this, dt)) { 440 for (var i = 0; i < this._tweenList.length; i++) { 441 this._tweenList[i].update(dt); 442 } 443 } 444 445 var frameEvents = this._frameEventQueue; 446 while (frameEvents.length > 0) { 447 var frameEvent = frameEvents.shift(); 448 this.ignoreFrameEvent = true; 449 this.callFrameEvent([frameEvent.bone, frameEvent.frameEventName, frameEvent.originFrameIndex, frameEvent.currentFrameIndex]); 450 this.ignoreFrameEvent = false; 451 } 452 453 var movementEvents = this._movementEventQueue; 454 while (movementEvents.length > 0) { 455 var movEvent = movementEvents.shift(); 456 this.callMovementEvent([movEvent.armature, movEvent.movementType, movEvent.movementID]); 457 } 458 }, 459 460 /** 461 * update will call this handler, you can handle your logic here 462 */ 463 updateHandler: function () { 464 var locCurrentPercent = this._currentPercent; 465 if (locCurrentPercent >= 1) { 466 switch (this._loopType) { 467 case ccs.ANIMATION_TYPE_NO_LOOP: 468 this._loopType = ccs.ANIMATION_TYPE_MAX; 469 this._currentFrame = (locCurrentPercent - 1) * this._nextFrameIndex; 470 locCurrentPercent = this._currentFrame / this._durationTween; 471 if (locCurrentPercent < 1.0) { 472 this._nextFrameIndex = this._durationTween; 473 this.movementEvent(this._armature, ccs.MovementEventType.start, this._movementID); 474 break; 475 } 476 case ccs.ANIMATION_TYPE_MAX: 477 case ccs.ANIMATION_TYPE_SINGLE_FRAME: 478 locCurrentPercent = 1; 479 this._isComplete = true; 480 this._isPlaying = false; 481 this.movementEvent(this._armature, ccs.MovementEventType.complete, this._movementID); 482 this.updateMovementList(); 483 break; 484 case ccs.ANIMATION_TYPE_TO_LOOP_FRONT: 485 this._loopType = ccs.ANIMATION_TYPE_LOOP_FRONT; 486 locCurrentPercent = ccs.fmodf(locCurrentPercent, 1); 487 this._currentFrame = this._nextFrameIndex == 0 ? 0 : ccs.fmodf(this._currentFrame, this._nextFrameIndex); 488 this._nextFrameIndex = this._durationTween > 0 ? this._durationTween : 1; 489 this.movementEvent(this, ccs.MovementEventType.start, this._movementID); 490 break; 491 default: 492 //locCurrentPercent = ccs.fmodf(locCurrentPercent, 1); 493 this._currentFrame = ccs.fmodf(this._currentFrame, this._nextFrameIndex); 494 this._toIndex = 0; 495 this.movementEvent(this._armature, ccs.MovementEventType.loopComplete, this._movementID); 496 break; 497 } 498 this._currentPercent = locCurrentPercent; 499 } 500 }, 501 502 /** 503 * Get current movementID 504 * @returns {String} 505 */ 506 getCurrentMovementID: function () { 507 if (this._isComplete) 508 return ""; 509 return this._movementID; 510 }, 511 512 /** 513 * connect a event 514 * @param {Object} target 515 * @param {function} callFunc 516 */ 517 setMovementEventCallFunc: function (callFunc, target) { 518 this._movementEvent = new ccs.AnimationEvent(target, callFunc); 519 }, 520 521 /** 522 * call event 523 * @param {Array} args 524 */ 525 callMovementEvent: function (args) { 526 if (this._movementEvent) { 527 this._movementEvent.setArguments(args); 528 this._movementEvent.call(); 529 } 530 }, 531 532 /** 533 * connect a event 534 * @param {Object} target 535 * @param {function} callFunc 536 */ 537 setFrameEventCallFunc: function (callFunc, target) { 538 this._frameEvent = new ccs.AnimationEvent(target, callFunc); 539 }, 540 541 /** 542 * call event 543 * @param {Array} args 544 */ 545 callFrameEvent: function (args) { 546 if (this._frameEvent) { 547 this._frameEvent.setArguments(args); 548 this._frameEvent.call(); 549 } 550 }, 551 552 movementEvent: function (armature, movementType, movementID) { 553 if (this._movementEvent) { 554 var event = new ccs.MovementEvent(); 555 event.armature = armature; 556 event.movementType = movementType; 557 event.movementID = movementID; 558 this._movementEventQueue.push(event); 559 } 560 }, 561 562 /** 563 * @param {ccs.Bone} bone 564 * @param {String} frameEventName 565 * @param {Number} originFrameIndex 566 * @param {Number} currentFrameIndex 567 */ 568 frameEvent: function (bone, frameEventName, originFrameIndex, currentFrameIndex) { 569 if (this._frameEvent) { 570 var frameEvent = new ccs.FrameEvent(); 571 frameEvent.bone = bone; 572 frameEvent.frameEventName = frameEventName; 573 frameEvent.originFrameIndex = originFrameIndex; 574 frameEvent.currentFrameIndex = currentFrameIndex; 575 this._frameEventQueue.push(frameEvent); 576 } 577 }, 578 579 /** 580 * animationData setter 581 * @param {ccs.AnimationData} aniData 582 */ 583 setAnimationData: function (aniData) { 584 this.animationData = aniData; 585 }, 586 587 /** 588 * animationData getter 589 * @return {ccs.AnimationData} 590 */ 591 getAnimationData: function () { 592 return this.animationData; 593 }, 594 /** 595 * userObject setter 596 * @param {Object} userObject 597 */ 598 setUserObject: function (userObject) { 599 this.userObject = userObject; 600 }, 601 602 /** 603 * userObject getter 604 * @return {Object} 605 */ 606 getUserObject: function () { 607 return this.userObject; 608 }, 609 610 /** 611 * Determines if the frame event is ignored 612 * @returns {boolean} 613 */ 614 isIgnoreFrameEvent: function () { 615 return this.ignoreFrameEvent; 616 }, 617 618 /** 619 * Sets whether the frame event is ignored 620 * @param {Boolean} bool 621 */ 622 setIgnoreFrameEvent: function (bool) { 623 this.ignoreFrameEvent = bool; 624 } 625 }); 626 627 var _p = ccs.ArmatureAnimation.prototype; 628 629 // Extended properties 630 /** @expose */ 631 _p.speedScale; 632 cc.defineGetterSetter(_p, "speedScale", _p.getSpeedScale, _p.setSpeedScale); 633 /** @expose */ 634 _p.animationScale; 635 cc.defineGetterSetter(_p, "animationScale", _p.getAnimationScale, _p.setAnimationScale); 636 637 _p = null; 638 639 /** 640 * allocates and initializes a ArmatureAnimation. 641 * @constructs 642 * @return {ccs.ArmatureAnimation} 643 * @example 644 * // example 645 * var animation = ccs.ArmatureAnimation.create(); 646 */ 647 ccs.ArmatureAnimation.create = function (armature) { 648 var animation = new ccs.ArmatureAnimation(); 649 if (animation && animation.init(armature)) { 650 return animation; 651 } 652 return null; 653 };