Animation sequencer revamp: animations can be added incrementally;

animations can be cued on the completion of the previous animation;
runnables can be provided for invocation upon animation completion or
in place of an animation in the chain.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1907 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2002-11-05 20:53:53 +00:00
parent d5dbc514ca
commit 3eba0dcf13
@@ -1,11 +1,13 @@
// //
// $Id: AnimationSequencer.java,v 1.1 2002/09/30 06:30:47 shaper Exp $ // $Id: AnimationSequencer.java,v 1.2 2002/11/05 20:53:53 mdb Exp $
package com.threerings.media.animation; package com.threerings.media.animation;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.util.ArrayList;
import com.threerings.media.Log; import com.threerings.media.Log;
/** /**
@@ -16,68 +18,105 @@ import com.threerings.media.Log;
public abstract class AnimationSequencer extends Animation public abstract class AnimationSequencer extends Animation
{ {
/** /**
* Constructs an animation sequencer that adds each of the supplied * Constructs an animation sequencer with the expectation that
* animations after regular intervals of the specified time. Note * animations will be added via subsequent calls to {@link
* that the first animation will be added immediately. * #addAnimation}.
*/ */
public AnimationSequencer (Animation[] anims, long delta) public AnimationSequencer ()
{ {
super(new Rectangle()); super(new Rectangle());
// save things off
_anims = anims;
_sdelta = delta;
} }
/** /**
* Constructs an animation sequencer that adds each of the supplied * Adds the supplied animation to the sequence with the given
* animations after the time specified for each animation has elapsed. * parameters. Note that this cannot be called after the animation
* Note that the first animation will be added immediately, though * sequence has begun executing without endangering your sanity and
* (for ease of coding and array indexing) it should have a slot in * the robustness of your code.
* the <code>delta</code> array despite the fact that the value *
* therein will be ignored. * @param anim the animation to be sequenced, or null if the
* completion action should be run immediately when this animation is
* ready to fired.
* @param delta the number of milliseconds following the start of the last
* animation currently in the sequence that this animation should be
* started; or -1 which means that this animation should be started
* when the last animation has completed.
* @param completionAction a runnable to be executed when this
* animation completes.
*/ */
public AnimationSequencer (Animation[] anims, long[] delta) public void addAnimation (
Animation anim, long delta, Runnable completionAction)
{ {
super(new Rectangle()); if (_finished) {
throw new IllegalStateException("Ack! Animation added when we were finished.");
}
AnimRecord arec = new AnimRecord(anim, delta, completionAction);
if (delta == -1) {
int size = _anims.size();
if (size == 0) {
// if there's no predecessor then this guy has nobody to
// wait for, so we run him immediately
arec.delta = 0;
} else {
((AnimRecord)_anims.get(size - 1)).dependent = arec;
}
}
_anims.add(arec);
_animsLeft++;
}
// save things off /**
_anims = anims; * Clears out the animations being managed by this sequencer.
_delta = delta; */
public void clear ()
{
_anims.clear();
_animsLeft = 0;
_lastidx = -1;
_lastStamp = 0;
}
/**
* Returns the number of animations being managed by this sequencer.
*/
public int getAnimationCount ()
{
return _anims.size();
} }
// documentation inherited // documentation inherited
public void tick (long tickStamp) public void tick (long tickStamp)
{ {
if (_start == 0) { if (_lastStamp == 0) {
// initialize our starting time _lastStamp = tickStamp;
_start = tickStamp;
} }
// TODO: revamp the below to keep track of the mark time for the
// next animation to be added rather than recalculating it every
// time we're ticked
// add all animations whose time has come // add all animations whose time has come
int acount = _anims.length; int acount = _anims.size();
long mark = _start; for (int ii = _lastidx + 1; ii < acount; ii++) {
for (int ii = 0; ii < acount; ii++) { AnimRecord arec = (AnimRecord)_anims.get(ii);
// calculate the time at which this animation is to be added
long delta = (_delta == null) ? _sdelta : _delta[ii];
// add in the delta for this animation, or if it's the first
// animation, we add it straightaway and ignore its delta
mark += ((ii == 0) ? 0 : delta);
if (_lastidx >= ii) { if (arec.readyToFire(tickStamp, _lastStamp)) {
// we've already added this animation, so move on to the next // note that we've advanced to the next animation
continue;
} else if (tickStamp >= mark) {
// add the animation
// Log.info("Adding animation [ii=" + ii +
// ", tickStamp=" + tickStamp + ", mark=" + mark + "].");
addAnimation(ii, _anims[ii], tickStamp);
_lastidx = ii; _lastidx = ii;
_lastStamp = tickStamp;
// Log.info("Adding animation [ii=" + ii +
// ", tickStamp=" + tickStamp + "].");
if (arec.anim != null) {
addAnimation(ii, arec.anim, tickStamp);
arec.anim.addAnimationObserver(arec);
} else if (arec.completionAction != null) {
try {
arec.completionAction.run();
} catch (Throwable t) {
Log.logStackTrace(t);
}
// if our last "animation" is not an animation at all,
// we need to finish now
animationDone();
}
} else { } else {
// it's not time to add this animation, and so all // it's not time to add this animation, and so all
@@ -85,9 +124,11 @@ public abstract class AnimationSequencer extends Animation
break; break;
} }
} }
}
// we're finished when we've added the last animation protected void animationDone ()
_finished = (_lastidx == acount - 1); {
_finished = (--_animsLeft == 0);
} }
/** /**
@@ -115,23 +156,66 @@ public abstract class AnimationSequencer extends Animation
// documentation inherited // documentation inherited
public void fastForward (long timeDelta) public void fastForward (long timeDelta)
{ {
_start += timeDelta; _lastStamp += timeDelta;
} }
/** The animations to be added in sequence. */ protected class AnimRecord
protected Animation[] _anims; implements AnimationObserver
{
public Animation anim;
public long delta;
public Runnable completionAction;
public AnimRecord dependent;
/** The time deltas in milliseconds between adding each animation, if public AnimRecord (
* the time deltas were specified on a per-animation basis. */ Animation anim, long delta, Runnable completionAction)
protected long[] _delta; {
this.anim = anim;
this.delta = delta;
this.completionAction = completionAction;
}
/** The single time delta in milliseconds between adding each public boolean readyToFire (long now, long lastStamp)
* animation, if a single time delta was given for all animations. */ {
protected long _sdelta; return (delta != -1) && (lastStamp + delta >= now);
}
/** The time at which the sequencer animation started its business. */ public void handleEvent (AnimationEvent event)
protected long _start; {
if (event instanceof AnimationCompletedEvent) {
// if the next animation is triggered on the completion of
// this animation, fiddle its delta so that it will claim
// to be ready to fire
if (dependent != null) {
dependent.delta = event.getWhen() - _lastStamp;
// kids, don't try this at home; we call tick()
// immediately so that this dependent animation and
// any simultaneous subsequent animations are fired
// immediately rather than waiting for the next call
// to tick
tick(event.getWhen());
}
// call the completion action, if there is one
if (completionAction != null) {
completionAction.run();
}
// make a note that this animation is complete
animationDone();
}
}
}
/** The animation records detailing the animations to be sequenced. */
protected ArrayList _anims = new ArrayList();
/** The number of animations remaining to be finished. */
protected int _animsLeft;
/** The index of the last animation that was added. */ /** The index of the last animation that was added. */
protected int _lastidx = -1; protected int _lastidx = -1;
/** The timestamp at which we added the last animation. */
protected long _lastStamp;
} }