diff --git a/starling/src/starling/display/MovieClip.as b/starling/src/starling/display/MovieClip.as index b46dc51e7..7e196c4ff 100644 --- a/starling/src/starling/display/MovieClip.as +++ b/starling/src/starling/display/MovieClip.as @@ -52,7 +52,9 @@ package starling.display private var mCurrentTime:Number; private var mCurrentFrame:int; private var mLoop:Boolean; + private var mBounceLoop:Boolean; private var mPlaying:Boolean; + private var mPlaybackDir:int; /** Creates a movie clip from the provided textures and with the specified default framerate. * The movie will have the size of the first frame. */ @@ -77,6 +79,8 @@ package starling.display mDefaultFrameDuration = 1.0 / fps; mLoop = true; mPlaying = true; + mBounceLoop = false; + mPlaybackDir = 1; mCurrentTime = 0.0; mCurrentFrame = 0; mTextures = textures.concat(); @@ -211,76 +215,139 @@ package starling.display // IAnimatable /** @inheritDoc */ - public function advanceTime(passedTime:Number):void - { - if (!mPlaying || passedTime <= 0.0) return; - - var finalFrame:int; - var previousFrame:int = mCurrentFrame; - var restTime:Number = 0.0; - var breakAfterFrame:Boolean = false; - var hasCompleteListener:Boolean = hasEventListener(Event.COMPLETE); - var dispatchCompleteEvent:Boolean = false; - var totalTime:Number = this.totalTime; - - if (mLoop && mCurrentTime >= totalTime) - { - mCurrentTime = 0.0; - mCurrentFrame = 0; - } - - if (mCurrentTime < totalTime) - { - mCurrentTime += passedTime; - finalFrame = mTextures.length - 1; - - while (mCurrentTime > mStartTimes[mCurrentFrame] + mDurations[mCurrentFrame]) - { - if (mCurrentFrame == finalFrame) - { - if (mLoop && !hasCompleteListener) - { - mCurrentTime -= totalTime; - mCurrentFrame = 0; - } - else - { - breakAfterFrame = true; - restTime = mCurrentTime - totalTime; - dispatchCompleteEvent = hasCompleteListener; - mCurrentFrame = finalFrame; - mCurrentTime = totalTime; - } - } - else - { - mCurrentFrame++; - } - - var sound:Sound = mSounds[mCurrentFrame]; - if (sound) sound.play(); - if (breakAfterFrame) break; - } - - // special case when we reach *exactly* the total time. - if (mCurrentFrame == finalFrame && mCurrentTime == totalTime) - dispatchCompleteEvent = hasCompleteListener; - } - - if (mCurrentFrame != previousFrame) - texture = mTextures[mCurrentFrame]; - - if (dispatchCompleteEvent) - dispatchEventWith(Event.COMPLETE); - - if (mLoop && restTime > 0.0) - advanceTime(restTime); - } - + public function advanceTime(passedTime:Number):void + { + if (!mPlaying || passedTime <= 0.0) return; + + var finalFrame:int = mTextures.length - 1; + var previousFrame:int = mCurrentFrame; + var restTime:Number = 0.0; + var breakAfterFrame:Boolean = false; + var hasCompleteListener:Boolean = hasEventListener(Event.COMPLETE); + var dispatchCompleteEvent:Boolean = false; + var totalTime:Number = this.totalTime; + var sound:Sound; + + if (mLoop) + { + if (mPlaybackDir == 1 && mCurrentTime >= totalTime) + { + mCurrentTime = 0.0; + mCurrentFrame = 0; + } + else if (mPlaybackDir == -1 && mCurrentTime == 0) + { + mCurrentTime = totalTime; + mCurrentFrame = mTextures.length - 1; + } + } + + // forward playback + if (mPlaybackDir == 1 && mCurrentTime < totalTime) + { + mCurrentTime += passedTime; + + while (mCurrentTime > mStartTimes[mCurrentFrame] + mDurations[mCurrentFrame]) + { + if (mCurrentFrame == finalFrame) + { + if (mLoop && !hasCompleteListener) + { + mCurrentTime -= totalTime; + mCurrentFrame = 0; + } + else + { + if (mBounceLoop) + { + mPlaybackDir = -1; + mCurrentTime = mStartTimes[mCurrentFrame]; + } + else + { + mCurrentTime = totalTime; + } + breakAfterFrame = true; + restTime = mCurrentTime - totalTime; + dispatchCompleteEvent = hasCompleteListener; + mCurrentFrame = finalFrame; + } + } + else + { + mCurrentFrame++; + } + + sound = mSounds[mCurrentFrame]; + if (sound) sound.play(); + if (breakAfterFrame) break; + } + + // special case when we reach *exactly* the total time. + if (mCurrentFrame == finalFrame && mCurrentTime == totalTime) + dispatchCompleteEvent = hasCompleteListener; + } + + // reverse playback + else if (mPlaybackDir == -1 && mCurrentTime > 0) + { + mCurrentTime -= passedTime; + + while (mCurrentTime <= mStartTimes[mCurrentFrame]) + { + if (mCurrentFrame == 0) + { + if (mLoop && !hasCompleteListener) + { + mCurrentTime += totalTime; + mCurrentFrame = finalFrame; + } + else + { + if (mBounceLoop) + { + mPlaybackDir = 1; + mCurrentTime = mStartTimes[1]; + } + else + { + mCurrentTime = 0; + } + breakAfterFrame = true; + restTime = -mCurrentTime; + dispatchCompleteEvent = hasCompleteListener; + mCurrentFrame = 0; + } + } + else + { + mCurrentFrame--; + } + + sound = mSounds[mCurrentFrame]; + if (sound) sound.play(); + if (breakAfterFrame) break; + } + + // special case when we reach *exactly* the start time. + if (mCurrentFrame == 0 && mCurrentTime == 0) + dispatchCompleteEvent = hasCompleteListener; + } + + if (mCurrentFrame != previousFrame) + texture = mTextures[mCurrentFrame]; + + if (dispatchCompleteEvent) + dispatchEventWith(Event.COMPLETE); + + if ((mLoop || mBounceLoop) && restTime) + advanceTime(restTime); + } + /** Indicates if a (non-looping) movie has come to its end. */ public function get isComplete():Boolean { - return !mLoop && mCurrentTime >= totalTime; + return !mLoop && !mBounceLoop && mCurrentTime >= totalTime; } // properties @@ -298,9 +365,45 @@ package starling.display /** The total number of frames. */ public function get numFrames():int { return mTextures.length; } - /** Indicates if the clip should loop. */ + /** Indicates if the clip should loop in one direction. + * If set to true bounceLoop will become false. */ public function get loop():Boolean { return mLoop; } - public function set loop(value:Boolean):void { mLoop = value; } + public function set loop(value:Boolean):void + { + mLoop = value; + if (mLoop) mBounceLoop = false; + } + + /** Indicates if the clip should loop forward and backward. + * If set to true loop will become false. */ + public function get bounceLoop():Boolean { return mBounceLoop; } + public function set bounceLoop(value:Boolean):void + { + mBounceLoop = value; + if (mBounceLoop) mLoop = false; + } + + /** Indicates if the clip will play forward or in reverse. + * If the clip is at the end of playback in its current direction, + * and playback direction is reversed, playback will restart. */ + public function get reverse():Boolean { return mPlaybackDir == -1; } + public function set reverse(value:Boolean):void + { + if ((value && mPlaybackDir == -1) || (!value && mPlaybackDir == 1)) return; + mPlaybackDir = value ? -1 : 1; + + // if already at the end of playback when reverse is set, restart playback + if (mPlaybackDir == 1 && mCurrentTime == totalTime) + { + mCurrentTime = 0.0; + mCurrentFrame = 0; + } + else if (mPlaybackDir == -1 && mCurrentTime == 0) + { + mCurrentTime = totalTime; + mCurrentFrame = mTextures.length - 1; + } + } /** The index of the frame that is currently displayed. */ public function get currentFrame():int { return mCurrentFrame; } @@ -343,9 +446,12 @@ package starling.display public function get isPlaying():Boolean { if (mPlaying) - return mLoop || mCurrentTime < totalTime; - else - return false; + { + if (mLoop || mBounceLoop) return true; + else if (mPlaybackDir == 1 && mCurrentTime < totalTime) return true; + else if (mPlaybackDir == -1 && mCurrentTime > 0) return true; + } + return false; } } } \ No newline at end of file