1 /**************************************************************************** 2 Copyright (c) 2013-2014 Chukong Technologies Inc. 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 /** 27 * ActionTimelineData 28 * @name ccs.ActionTimelineData 29 * @extend ccs.Class 30 * @class 31 * 32 */ 33 ccs.ActionTimelineData = ccs.Class.extend({ 34 35 _actionTag: 0, 36 37 ctor: function(actionTag){ 38 this._init(actionTag); 39 }, 40 41 _init: function(actionTag){ 42 this._actionTag = actionTag; 43 return true; 44 }, 45 46 /** 47 * Set the action tag. 48 * @param {number} actionTag 49 */ 50 setActionTag: function(actionTag){ 51 this._actionTag = actionTag; 52 }, 53 54 /** 55 * Gets the action tag. 56 */ 57 getActionTag: function(){ 58 return this._actionTag; 59 } 60 61 }); 62 63 ccs.AnimationInfo = function(name, start, end){ 64 this.name = name; 65 this.startIndex = start; 66 this.endIndex = end; 67 }; 68 69 ccs.ComExtensionData = ccs.Component.extend({ 70 71 _customProperty: null, 72 _timelineData: null, 73 _name: "ComExtensionData", 74 75 ctor: function(){ 76 this._customProperty = ""; 77 this._timelineData = new ccs.ActionTimelineData(0); 78 return true; 79 }, 80 81 setActionTag: function(actionTag){ 82 this._timelineData.setActionTag(actionTag); 83 }, 84 85 getActionTag: function(){ 86 return this._timelineData.getActionTag(); 87 }, 88 89 setCustomProperty: function(customProperty){ 90 this._customProperty = customProperty; 91 }, 92 93 getCustomProperty: function(){ 94 return this._customProperty; 95 } 96 97 }); 98 99 ccs.ComExtensionData.create = function(){ 100 return new ccs.ComExtensionData(); 101 }; 102 103 /** 104 * Create new ActionTimelineData. 105 * 106 * @deprecated v3.0, please use new ccs.ActionTimelineData() instead. 107 * 108 * @name ccs.ActionTimelineData.create 109 * @function 110 * @param actionTag 111 * @returns {ccs.ActionTimelineData} 112 */ 113 ccs.ActionTimelineData.create = function(actionTag){ 114 return new ccs.ActionTimelineData(actionTag); 115 }; 116 117 118 /** 119 * ActionTimeline 120 * @class 121 * @extend cc.Action 122 * 123 * @property gotoFrameAndPlay 124 * @property gotoFrameAndPause 125 */ 126 ccs.ActionTimeline = cc.Action.extend({ 127 128 _timelineMap: null, 129 _timelineList: null, 130 _duration: 0, 131 _time: null, 132 _timeSpeed: 1, 133 _frameInternal: 1/60, 134 _playing: false, 135 _currentFrame: 0, 136 _startFrame: 0, 137 _endFrame: 0, 138 _loop: null, 139 _frameEventListener: null, 140 _animationInfos: null, 141 _lastFrameListener: null, 142 143 ctor: function(){ 144 cc.Action.prototype.ctor.call(this); 145 this._timelineMap = {}; 146 this._timelineList = []; 147 this._animationInfos = {}; 148 this.init(); 149 }, 150 151 _gotoFrame: function(frameIndex){ 152 var size = this._timelineList.length; 153 for(var i = 0; i < size; i++) 154 { 155 this._timelineList[i]._gotoFrame(frameIndex); 156 } 157 }, 158 159 _stepToFrame: function(frameIndex){ 160 var size = this._timelineList.length; 161 for(var i = 0; i < size; i++){ 162 this._timelineList[i]._stepToFrame(frameIndex); 163 } 164 }, 165 166 //emit frame event, call it when enter a frame 167 _emitFrameEvent: function(frame){ 168 if(this._frameEventListener){ 169 this._frameEventListener(frame); 170 } 171 }, 172 173 init: function(){ 174 return true; 175 }, 176 177 /** 178 * Goto the specified frame index, and start playing from this index. 179 * @param startIndex The animation will play from this index. 180 * @param [endIndex=] The animation will end at this index. 181 * @param [currentFrameIndex=] set current frame index. 182 * @param [loop=] Whether or not the animation need loop. 183 */ 184 gotoFrameAndPlay: function(startIndex, endIndex, currentFrameIndex, loop){ 185 //Consolidation parameters 186 var i = 0, 187 argLen = arguments.length; 188 var num = [], 189 bool; 190 for(i; i<argLen; i++){ 191 if(typeof arguments[i] === "boolean"){ 192 bool = arguments[i]; 193 }else{ 194 num.push(arguments[i]); 195 } 196 } 197 startIndex = num[0]; 198 endIndex = num[1] !== undefined ? num[1] : this._duration; 199 currentFrameIndex = num[2] || startIndex; 200 loop = bool!=null ? bool : true; 201 202 this._startFrame = startIndex; 203 this._endFrame = endIndex; 204 this._currentFrame = currentFrameIndex; 205 this._loop = loop; 206 this._time = this._currentFrame * this._frameInternal; 207 208 this.resume(); 209 this._gotoFrame(this._currentFrame); 210 }, 211 212 /** 213 * Goto the specified frame index, and pause at this index. 214 * @param startIndex The animation will pause at this index. 215 */ 216 gotoFrameAndPause: function(startIndex){ 217 this._startFrame = this._currentFrame = startIndex; 218 this._time = this._currentFrame * this._frameInternal; 219 220 this.pause(); 221 this._gotoFrame(this._currentFrame); 222 }, 223 224 /** 225 * Pause the animation. 226 */ 227 pause: function(){ 228 this._playing = false; 229 }, 230 231 /** 232 * Resume the animation. 233 */ 234 resume: function(){ 235 this._playing = true; 236 }, 237 238 /** 239 * Whether or not Action is playing. 240 */ 241 isPlaying: function(){ 242 return this._playing; 243 }, 244 245 /** 246 * Set the animation speed, this will speed up or slow down the speed. 247 * @param {number} speed 248 */ 249 setTimeSpeed: function(speed){ 250 this._timeSpeed = speed; 251 }, 252 253 /** 254 * Get current animation speed. 255 * @returns {number} 256 */ 257 getTimeSpeed: function(){ 258 return this._timeSpeed; 259 }, 260 261 /** 262 * duration of the whole action 263 * @param {number} duration 264 */ 265 setDuration: function(duration){ 266 this._duration = duration; 267 }, 268 269 /** 270 * Get current animation duration. 271 * @returns {number} 272 */ 273 getDuration: function(){ 274 return this._duration; 275 }, 276 277 /** 278 * Start frame index of this action 279 * @returns {number} 280 */ 281 getStartFrame: function(){ 282 return this._startFrame; 283 }, 284 285 /** 286 * End frame of this action. 287 * When action play to this frame, if action is not loop, then it will stop, 288 * or it will play from start frame again. 289 * @returns {number} 290 */ 291 getEndFrame: function(){ 292 return this._endFrame; 293 }, 294 295 /** 296 * Set current frame index, this will cause action plays to this frame. 297 */ 298 setCurrentFrame: function(frameIndex){ 299 if (frameIndex >= this._startFrame && frameIndex <= this._endFrame){ 300 this._currentFrame = frameIndex; 301 this._time = this._currentFrame * this._frameInternal; 302 }else{ 303 cc.log("frame index is not between start frame and end frame"); 304 } 305 306 }, 307 308 /** 309 * Get current frame. 310 * @returns {number} 311 */ 312 getCurrentFrame: function(){ 313 return this._currentFrame; 314 }, 315 316 /** 317 * add Timeline to ActionTimeline 318 * @param {ccs.Timeline} timeline 319 */ 320 addTimeline: function(timeline){ 321 var tag = timeline.getActionTag(); 322 if (!this._timelineMap[tag]) { 323 this._timelineMap[tag] = []; 324 } 325 326 if (this._timelineMap[tag].indexOf(timeline) === -1) { 327 this._timelineList.push(timeline); 328 this._timelineMap[tag].push(timeline); 329 timeline.setActionTimeline(this); 330 } 331 332 }, 333 334 /** 335 * remove Timeline to ActionTimeline 336 * @param {ccs.Timeline} timeline 337 */ 338 removeTimeline: function(timeline){ 339 var tag = timeline.getActionTag(); 340 if (this._timelineMap[tag]) { 341 if(this._timelineMap[tag].some(function(item){ 342 if(item === timeline) 343 return true; 344 })) { 345 cc.arrayRemoveObject(this._timelineMap[tag], timeline); 346 cc.arrayRemoveObject(this._timelineList, timeline); 347 timeline.setActionTimeline(null); 348 } 349 } 350 }, 351 352 /** 353 * Gets the timeline list 354 * @returns {array | null} 355 */ 356 getTimelines: function(){ 357 return this._timelineList; 358 }, 359 360 /** 361 * Set the Frame event 362 * @param {function} listener 363 */ 364 setFrameEventCallFunc: function(listener){ 365 this._frameEventListener = listener; 366 }, 367 368 /** 369 * remove event 370 */ 371 clearFrameEventCallFunc: function(){ 372 this._frameEventListener = null; 373 }, 374 375 /** 376 * Clone this timeline 377 * @returns {ccs.ActionTimeline} 378 */ 379 clone: function(){ 380 var newAction = new ccs.ActionTimeline(); 381 newAction.setDuration(this._duration); 382 newAction.setTimeSpeed(this._timeSpeed); 383 384 for (var a in this._timelineMap){ 385 var timelines = this._timelineMap[a]; 386 for(var b in timelines) 387 { 388 var timeline = timelines[b]; 389 var newTimeline = timeline.clone(); 390 newAction.addTimeline(newTimeline); 391 } 392 } 393 394 return newAction; 395 396 }, 397 398 /** 399 * Reverse is not defined; 400 * @returns {null} 401 */ 402 reverse: function(){ 403 return null; 404 }, 405 406 /** 407 * Stepping of this time line. 408 * @param {number} delta 409 */ 410 step: function(delta){ 411 if (!this._playing || this._timelineMap.length === 0 || this._duration === 0) 412 { 413 return; 414 } 415 416 this._time += delta * this._timeSpeed; 417 var endoffset = this._time - this._endFrame * this._frameInternal; 418 419 if(endoffset < this._frameInternal){ 420 this._currentFrame = Math.floor(this._time / this._frameInternal); 421 this._stepToFrame(this._currentFrame); 422 if(endoffset >= 0 && this._lastFrameListener) 423 this._lastFrameListener(); 424 }else{ 425 this._playing = this._loop; 426 if(!this._playing){ 427 this._time = this._endFrame * this._frameInternal; 428 if (this._currentFrame != this._endFrame){ 429 this._currentFrame = this._endFrame; 430 this._stepToFrame(this._currentFrame); 431 if(this._lastFrameListener) 432 this._lastFrameListener(); 433 } 434 }else 435 this.gotoFrameAndPlay(this._startFrame, this._endFrame, this._loop); 436 } 437 438 }, 439 440 _foreachNodeDescendant: function(parent, callback){ 441 callback(parent); 442 443 var children = parent.getChildren(); 444 for (var i=0; i<children.length; i++) 445 { 446 var child = children[i]; 447 this._foreachNodeDescendant(child, callback); 448 } 449 }, 450 451 /** 452 * start with node. 453 * @param {cc.Node} target 454 */ 455 startWithTarget: function(target){ 456 cc.Action.prototype.startWithTarget.call(this, target); 457 458 var self = this; 459 var callback = function(child){ 460 var data = child.getComponent("ComExtensionData"); 461 462 if(data) { 463 var actionTag = data.getActionTag(); 464 if(self._timelineMap[actionTag]) { 465 var timelines = self._timelineMap[actionTag]; 466 for (var i=0; i<timelines.length; i++) { 467 var timeline = timelines[i]; 468 timeline.setNode(child); 469 } 470 } 471 } 472 }; 473 474 this._foreachNodeDescendant(target, callback); 475 }, 476 477 /** 478 * Whether or not complete 479 * @returns {boolean} 480 */ 481 isDone: function(){ 482 return false; 483 }, 484 485 /** 486 * @param {String} name 487 * @param {Boolean} loop 488 */ 489 play: function(name, loop){ 490 var info = this._animationInfos[name]; 491 if (!info) 492 return cc.log("Can't find animation info for %s", name); 493 494 this.gotoFrameAndPlay(info.startIndex, info.endIndex, loop); 495 }, 496 497 /** 498 * Add animationInfo 499 * @param {Object} info 500 */ 501 addAnimationInfo: function(info){ 502 this._animationInfos[info.name] = info; 503 }, 504 505 /** 506 * Remove animationInfo 507 * @param {String} name 508 */ 509 removeAnimationInfo: function(name){ 510 delete this._animationInfos[name]; 511 }, 512 513 isAnimationInfoExists: function(name){ 514 return this._animationInfos[name]; 515 }, 516 517 getAnimationInfo: function(name){ 518 return this._animationInfos[name]; 519 }, 520 521 setLastFrameCallFunc: function(listener){ 522 this._lastFrameListener = listener; 523 }, 524 525 clearLastFrameCallFunc: function(){ 526 this._lastFrameListener = null; 527 } 528 }); 529 530 /** 531 * create new ActionTimeline 532 * 533 * @deprecated v3.0, please use new ccs.ActionTimeline() instead. 534 * 535 * @name ccs.ActionTimeline.create 536 * @function 537 * @returns {ccs.ActionTimeline} 538 */ 539 ccs.ActionTimeline.create = function(){ 540 return new ccs.ActionTimeline(); 541 }; 542