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 * Base class for ccs.Tween objects. 27 * @class 28 * @extends ccs.ProcessBase 29 */ 30 ccs.Tween = ccs.ProcessBase.extend(/** @lends ccs.Tween# */{ 31 _tweenData:null, 32 _to:null, 33 _from:null, 34 _between:null, 35 _movementBoneData:null, 36 _bone:null, 37 _frameTweenEasing:0, 38 _betweenDuration:0, 39 _totalDuration:0, 40 _toIndex:0, 41 _fromIndex:0, 42 _animation:null, 43 _passLastFrame:false, 44 ctor:function () { 45 ccs.ProcessBase.prototype.ctor.call(this); 46 this._tweenData = null; 47 this._to = null; 48 this._from = null; 49 this._between = null; 50 this._bone = null; 51 this._movementBoneData = null; 52 this._frameTweenEasing = ccs.TweenType.linear; 53 this._toIndex = 0; 54 this._fromIndex = 0; 55 this._animation = null; 56 this._passLastFrame = false; 57 }, 58 59 /** 60 * init with a CCBone 61 * @param {ccs.Bone} bone 62 * @return {Boolean} 63 */ 64 init:function (bone) { 65 this._from = new ccs.FrameData(); 66 this._between = new ccs.FrameData(); 67 68 this._bone = bone; 69 this._tweenData = this._bone.getTweenData(); 70 this._tweenData.displayIndex = -1; 71 this._animation = this._bone.getArmature() != null ? this._bone.getArmature().getAnimation() : null; 72 return true; 73 }, 74 75 /** 76 * play animation by animation name. 77 * @param {Number} animationName The animation name you want to play 78 * @param {Number} durationTo 79 * he frames between two animation changing-over.It's meaning is changing to this animation need how many frames 80 * -1 : use the value from CCMovementData get from flash design panel 81 * @param {Number} durationTween he 82 * frame count you want to play in the game.if _durationTween is 80, then the animation will played 80 frames in a loop 83 * -1 : use the value from CCMovementData get from flash design panel 84 * @param {Number} loop 85 * Whether the animation is loop. 86 * loop < 0 : use the value from CCMovementData get from flash design panel 87 * loop = 0 : this animation is not loop 88 * loop > 0 : this animation is loop 89 * @param {Number} tweenEasing 90 * CCTween easing is used for calculate easing effect 91 * TWEEN_EASING_MAX : use the value from CCMovementData get from flash design panel 92 * -1 : fade out 93 * 0 : line 94 * 1 : fade in 95 * 2 : fade in and out 96 */ 97 play:function (movementBoneData, durationTo, durationTween, loop, tweenEasing) { 98 ccs.ProcessBase.prototype.play.call(this, durationTo, durationTween, loop, tweenEasing); 99 100 if(loop){ 101 this._loopType = CC_ANIMATION_TYPE_TO_LOOP_FRONT; 102 }else{ 103 this._loopType = CC_ANIMATION_TYPE_NO_LOOP; 104 } 105 106 this._totalDuration = 0; 107 this._betweenDuration = 0; 108 this._fromIndex = this._toIndex = 0; 109 110 var difMovement = movementBoneData != this._movementBoneData; 111 this._movementBoneData = movementBoneData; 112 this._rawDuration = this._movementBoneData.duration; 113 var nextKeyFrame = this._movementBoneData.getFrameData(0); 114 this._tweenData.displayIndex = nextKeyFrame.displayIndex; 115 116 if (this._bone.getArmature().getArmatureData().dataVersion >= ccs.CONST_VERSION_COMBINED) { 117 ccs.TransformHelp.nodeSub(this._tweenData, this._bone.getBoneData()); 118 this._tweenData.scaleX += 1; 119 this._tweenData.scaleY += 1; 120 } 121 122 if (this._rawDuration==0) { 123 this._loopType = CC_ANIMATION_TYPE_SINGLE_FRAME; 124 if (durationTo == 0) { 125 this.setBetween(nextKeyFrame, nextKeyFrame); 126 } else { 127 this.setBetween(this._tweenData, nextKeyFrame); 128 } 129 this._frameTweenEasing = ccs.TweenType.linear; 130 } 131 else if (this._movementBoneData.frameList.length > 1) { 132 this._durationTween = durationTween * this._movementBoneData.scale; 133 if (loop && this._movementBoneData.delay != 0) { 134 this.setBetween(this._tweenData, this.tweenNodeTo(this.updateFrameData(1 - this._movementBoneData.delay), this._between)); 135 } 136 else { 137 if (!difMovement || durationTo == 0) 138 this.setBetween(nextKeyFrame, nextKeyFrame); 139 else 140 this.setBetween(this._tweenData, nextKeyFrame); 141 } 142 } 143 this.tweenNodeTo(0); 144 }, 145 146 gotoAndPlay: function (frameIndex) { 147 ccs.ProcessBase.prototype.gotoFrame.call(this, frameIndex); 148 this._totalDuration = 0; 149 this._betweenDuration = 0; 150 this._fromIndex = this._toIndex = 0; 151 this._isPlaying = true; 152 this._isComplete = this._isPause = false; 153 this._currentPercent = this._curFrameIndex / this._rawDuration; 154 this._currentFrame = this._nextFrameIndex * this._currentPercent; 155 }, 156 157 gotoAndPause: function (frameIndex) { 158 this.gotoAndPlay(frameIndex); 159 this.pause(); 160 }, 161 162 /** 163 * update will call this handler, you can handle your logic here 164 */ 165 updateHandler:function () { 166 var locCurrentPercent = this._currentPercent; 167 var locLoopType = this._loopType; 168 if (locCurrentPercent >= 1) { 169 switch (locLoopType) { 170 case CC_ANIMATION_TYPE_SINGLE_FRAME: 171 locCurrentPercent = 1; 172 this._isComplete = true; 173 this._isPlaying = false; 174 break; 175 case CC_ANIMATION_TYPE_NO_LOOP: 176 locLoopType = CC_ANIMATION_TYPE_MAX; 177 if (this._durationTween <= 0) { 178 locCurrentPercent = 1; 179 } 180 else { 181 locCurrentPercent = (locCurrentPercent - 1) * this._nextFrameIndex / this._durationTween; 182 } 183 if (locCurrentPercent >= 1) { 184 locCurrentPercent = 1; 185 this._isComplete = true; 186 this._isPlaying = false; 187 break; 188 } 189 else { 190 this._nextFrameIndex = this._durationTween; 191 this._currentFrame = locCurrentPercent * this._nextFrameIndex; 192 this._totalDuration = 0; 193 this._betweenDuration = 0; 194 this._fromIndex = this._toIndex = 0; 195 break; 196 } 197 case CC_ANIMATION_TYPE_TO_LOOP_FRONT: 198 locLoopType = CC_ANIMATION_TYPE_LOOP_FRONT; 199 this._nextFrameIndex = this._durationTween > 0 ? this._durationTween : 1; 200 if (this._movementBoneData.delay != 0) { 201 this._currentFrame = (1 - this._movementBoneData.delay) * this._nextFrameIndex; 202 locCurrentPercent = this._currentFrame / this._nextFrameIndex; 203 204 } else { 205 locCurrentPercent = 0; 206 this._currentFrame = 0; 207 } 208 209 this._totalDuration = 0; 210 this._betweenDuration = 0; 211 this._fromIndex = this._toIndex = 0; 212 break; 213 case CC_ANIMATION_TYPE_MAX: 214 locCurrentPercent = 1; 215 this._isComplete = true; 216 this._isPlaying = false; 217 break; 218 default: 219 this._currentFrame = ccs.fmodf(this._currentFrame, this._nextFrameIndex); 220 break; 221 } 222 } 223 224 if (locCurrentPercent < 1 && locLoopType < CC_ANIMATION_TYPE_TO_LOOP_BACK) { 225 locCurrentPercent = Math.sin(locCurrentPercent * cc.PI / 2); 226 } 227 228 this._currentPercent = locCurrentPercent; 229 this._loopType = locLoopType; 230 231 if (locLoopType > CC_ANIMATION_TYPE_TO_LOOP_BACK) { 232 locCurrentPercent = this.updateFrameData(locCurrentPercent); 233 } 234 if (this._frameTweenEasing != ccs.TweenType.tweenEasingMax) { 235 this.tweenNodeTo(locCurrentPercent); 236 } 237 }, 238 239 /** 240 * Calculate the between value of _from and _to, and give it to between frame data 241 * @param {ccs.FrameData} from 242 * @param {ccs.FrameData} to 243 */ 244 setBetween:function (from, to, limit) { 245 if (typeof limit == "undefined") { 246 limit = true; 247 } 248 do 249 { 250 if (from.displayIndex < 0 && to.displayIndex >= 0) { 251 this._from.copy(to); 252 this._between.subtract(to, to, limit); 253 break; 254 } 255 if (to.displayIndex < 0 && from.displayIndex >= 0) { 256 this._from.copy(from); 257 this._between.subtract(to, to, limit); 258 break; 259 } 260 this._from.copy(from); 261 this._between.subtract(from, to, limit); 262 } while (0); 263 if (!from.isTween){ 264 this._tweenData.copy(from); 265 this._tweenData.isTween = true; 266 } 267 this.arriveKeyFrame(from); 268 }, 269 270 /** 271 * Update display index and process the key frame event when arrived a key frame 272 * @param {ccs.FrameData} keyFrameData 273 */ 274 arriveKeyFrame:function (keyFrameData) { 275 if (keyFrameData) { 276 var locBone = this._bone; 277 var displayIndex = keyFrameData.displayIndex; 278 var displayManager = locBone.getDisplayManager(); 279 if (!displayManager.getForceChangeDisplay()) { 280 displayManager.changeDisplayByIndex(displayIndex, false); 281 282 } 283 this._tweenData.zOrder = keyFrameData.zOrder; 284 locBone.updateZOrder(); 285 locBone.setBlendType(keyFrameData.blendType); 286 var childAramture = locBone.getChildArmature(); 287 if (childAramture) { 288 if (keyFrameData.movement != "") { 289 childAramture.getAnimation().play(keyFrameData.movement); 290 } 291 } 292 } 293 }, 294 295 /** 296 * According to the percent to calculate current CCFrameData with tween effect 297 * @param {Number} percent 298 * @param {ccs.FrameData} node 299 * @return {ccs.FrameData} 300 */ 301 tweenNodeTo:function (percent, node) { 302 if (!node) { 303 node = this._tweenData; 304 } 305 var locFrom = this._from; 306 var locBetween = this._between; 307 if (!locFrom.isTween){ 308 percent = 0; 309 } 310 node.x = locFrom.x + percent * locBetween.x; 311 node.y = locFrom.y + percent * locBetween.y; 312 node.scaleX = locFrom.scaleX + percent * locBetween.scaleX; 313 node.scaleY = locFrom.scaleY + percent * locBetween.scaleY; 314 node.skewX = locFrom.skewX + percent * locBetween.skewX; 315 node.skewY = locFrom.skewY + percent * locBetween.skewY; 316 317 this._bone.setTransformDirty(true); 318 if (node && locBetween.isUseColorInfo) 319 this.tweenColorTo(percent, node); 320 321 return node; 322 }, 323 324 tweenColorTo:function(percent,node){ 325 var locFrom = this._from; 326 var locBetween = this._between; 327 node.a = locFrom.a + percent * locBetween.a; 328 node.r = locFrom.r + percent * locBetween.r; 329 node.g = locFrom.g + percent * locBetween.g; 330 node.b = locFrom.b + percent * locBetween.b; 331 this._bone.updateColor(); 332 }, 333 334 /** 335 * Calculate which frame arrived, and if current frame have event, then call the event listener 336 * @param {Number} currentPercent 337 * @param {Boolean} activeFrame 338 * @return {Number} 339 */ 340 updateFrameData:function (currentPercent) { 341 if (currentPercent > 1 && this._movementBoneData.delay != 0) { 342 currentPercent = ccs.fmodf(currentPercent,1); 343 } 344 var playedTime = this._rawDuration * currentPercent; 345 var from, to; 346 var locTotalDuration = this._totalDuration,locBetweenDuration = this._betweenDuration, locToIndex = this._toIndex; 347 // if play to current frame's front or back, then find current frame again 348 if (playedTime < locTotalDuration || playedTime >= locTotalDuration + locBetweenDuration) { 349 /* 350 * get frame length, if this._toIndex >= _length, then set this._toIndex to 0, start anew. 351 * this._toIndex is next index will play 352 */ 353 var length = this._movementBoneData.frameList.length; 354 var frames = this._movementBoneData.frameList; 355 if (playedTime < frames[0].frameID){ 356 from = to = frames[0]; 357 this.setBetween(from, to); 358 return currentPercent; 359 } 360 else if (playedTime >= frames[length - 1].frameID) { 361 if (this._passLastFrame) { 362 from = to = frames[length - 1]; 363 this.setBetween(from, to); 364 return currentPercent; 365 } 366 this._passLastFrame = true; 367 } else { 368 this._passLastFrame = false; 369 } 370 371 do { 372 this._fromIndex = locToIndex; 373 from = frames[this._fromIndex]; 374 locTotalDuration = from.frameID; 375 locToIndex = this._fromIndex + 1; 376 if (locToIndex >= length) { 377 locToIndex = 0; 378 } 379 to = frames[locToIndex]; 380 381 //! Guaranteed to trigger frame event 382 if(from.event&& !this._animation.isIgnoreFrameEvent()){ 383 this._animation.frameEvent(this._bone, from.event,from.frameID, playedTime); 384 } 385 386 if (playedTime == from.frameID|| (this._passLastFrame && this._fromIndex == length-1)){ 387 break; 388 } 389 } 390 while (playedTime < from.frameID || playedTime >= to.frameID); 391 392 locBetweenDuration = to.frameID - from.frameID; 393 this._frameTweenEasing = from.tweenEasing; 394 this.setBetween(from, to, false); 395 this._totalDuration = locTotalDuration; 396 this._betweenDuration = locBetweenDuration; 397 this._toIndex = locToIndex; 398 } 399 400 currentPercent = locBetweenDuration == 0 ? 0 : (playedTime - locTotalDuration) / locBetweenDuration; 401 402 /* 403 * if frame tween easing equal to TWEEN_EASING_MAX, then it will not do tween. 404 */ 405 var tweenType = (this._frameTweenEasing != ccs.TweenType.linear) ? this._frameTweenEasing : this._tweenEasing; 406 if (tweenType != ccs.TweenType.tweenEasingMax&&tweenType != ccs.TweenType.linear) { 407 currentPercent = ccs.TweenFunction.tweenTo(0, 1, currentPercent, 1, tweenType); 408 } 409 return currentPercent; 410 }, 411 412 /** 413 * animation setter 414 * @param {ccs.ArmatureAnimation} animation 415 */ 416 setAnimation:function (animation) { 417 this._animation = animation; 418 }, 419 420 /** 421 * animation getter 422 * @return {ccs.ArmatureAnimation} 423 */ 424 getAnimation:function () { 425 return this._animation; 426 }, 427 428 release:function () { 429 this._from = null; 430 this._between = null; 431 } 432 }); 433 434 /** 435 * allocates and initializes a ArmatureAnimation. 436 * @constructs 437 * @param {ccs.Bone} bone 438 * @return {ccs.ArmatureAnimation} 439 * @example 440 * // example 441 * var animation = ccs.ArmatureAnimation.create(); 442 */ 443 ccs.Tween.create = function (bone) { 444 var tween = new ccs.Tween(); 445 if (tween && tween.init(bone)) { 446 return tween; 447 } 448 return null; 449 }; 450