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