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