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