Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added reverse playback and "bounce" looping (back and forth) #343

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 178 additions & 72 deletions starling/src/starling/display/MovieClip.as
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand All @@ -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 <code>true</code> bounceLoop will become <code>false</code>. */
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 <code>true</code> loop will become <code>false</code>. */
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; }
Expand Down Expand Up @@ -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;
}
}
}