diff --git a/src/java/com/threerings/cast/CharacterSprite.java b/src/java/com/threerings/cast/CharacterSprite.java
index cbdd978be..66856ba4e 100644
--- a/src/java/com/threerings/cast/CharacterSprite.java
+++ b/src/java/com/threerings/cast/CharacterSprite.java
@@ -1,5 +1,5 @@
//
-// $Id: CharacterSprite.java,v 1.8 2001/08/21 19:40:30 mdb Exp $
+// $Id: CharacterSprite.java,v 1.9 2001/09/13 19:10:26 mdb Exp $
package com.threerings.miso.scene;
@@ -18,7 +18,7 @@ public class AmbulatorySprite extends Sprite implements Traverser
/**
* Construct an AmbulatorySprite, with a multi-frame
* image associated with each of the eight compass directions. The
- * array should be in the order defined by the Path
+ * array should be in the order defined by the Sprite
* direction constants (SW, W, NW, N, NE, E, SE, S).
*
* @param x the sprite x-position in pixels.
@@ -30,43 +30,22 @@ public class AmbulatorySprite extends Sprite implements Traverser
{
super(x, y);
+ // keep track of these
_anims = anims;
- _dir = Path.DIR_SOUTH;
- setFrames(_anims[Path.DIR_NORTH]);
+ // give ourselves an initial orientation
+ setOrientation(DIR_NORTH);
+
+ // we only animate when we're moving
+ setAnimationMode(MOVEMENT_CUED);
}
- /**
- * Alter the sprite's direction to reflect the direction the
- * destination point lies in before calling the superclass's
- * setDestination method.
- *
- * @param x the destination x-position.
- * @param y the destination y-position.
- */
- protected void moveAlongPath ()
+ public void setOrientation (int orient)
{
- // select the new path node
- super.moveAlongPath();
-
- // bail if we're at the end of the path
- if (_dest == null) {
- return;
- }
+ super.setOrientation(orient);
// update the sprite frames to reflect the direction
- setFrames(_anims[_dir = _dest.dir]);
-
- // start tile animation to show movement
- setAnimationDelay(0);
- }
-
- public void stop ()
- {
- super.stop();
-
- // stop any walking animation
- setAnimationDelay(ANIM_NONE);
+ setFrames(_anims[_orient]);
}
public boolean canTraverse (Tile tile)
@@ -77,7 +56,4 @@ public class AmbulatorySprite extends Sprite implements Traverser
/** The animation frames for the sprite facing each direction. */
protected MultiFrameImage[] _anims;
-
- /** The direction the sprite is currently facing. */
- protected int _dir;
}
diff --git a/src/java/com/threerings/media/sprite/AnimationManager.java b/src/java/com/threerings/media/sprite/AnimationManager.java
index 72798b5be..22edfdc23 100644
--- a/src/java/com/threerings/media/sprite/AnimationManager.java
+++ b/src/java/com/threerings/media/sprite/AnimationManager.java
@@ -1,5 +1,5 @@
//
-// $Id: AnimationManager.java,v 1.15 2001/08/23 00:23:58 shaper Exp $
+// $Id: AnimationManager.java,v 1.16 2001/09/13 19:10:26 mdb Exp $
package com.threerings.media.sprite;
@@ -110,8 +110,11 @@ public class AnimationManager
*/
protected void tick ()
{
+ // every tick should have a timestamp associated with it
+ long now = System.currentTimeMillis();
+
// call tick on all sprites
- _spritemgr.tick();
+ _spritemgr.tick(now);
// invalidate screen-rects dirtied by sprites
DirtyRectList rects = _spritemgr.getDirtyRects();
diff --git a/src/java/com/threerings/media/sprite/Sprite.java b/src/java/com/threerings/media/sprite/Sprite.java
index 01f9371d1..b42cb66fd 100644
--- a/src/java/com/threerings/media/sprite/Sprite.java
+++ b/src/java/com/threerings/media/sprite/Sprite.java
@@ -1,5 +1,5 @@
//
-// $Id: Sprite.java,v 1.19 2001/09/07 23:01:53 shaper Exp $
+// $Id: Sprite.java,v 1.20 2001/09/13 19:10:26 mdb Exp $
package com.threerings.media.sprite;
@@ -8,7 +8,6 @@ import java.util.ArrayList;
import java.util.Iterator;
import com.threerings.media.Log;
-import com.threerings.media.util.MathUtil;
/**
* The sprite class represents a single moveable object in an animated
@@ -17,6 +16,38 @@ import com.threerings.media.util.MathUtil;
*/
public class Sprite
{
+ /** The number of distinct directions. */
+ public static final int NUM_DIRECTIONS = 8;
+
+ /** Direction constants. */
+ public static final int DIR_NONE = -1;
+ public static final int DIR_SOUTHWEST = 0;
+ public static final int DIR_WEST = 1;
+ public static final int DIR_NORTHWEST = 2;
+ public static final int DIR_NORTH = 3;
+ public static final int DIR_NORTHEAST = 4;
+ public static final int DIR_EAST = 5;
+ public static final int DIR_SOUTHEAST = 6;
+ public static final int DIR_SOUTH = 7;
+
+ /** String translations for the direction constants. */
+ public static String[] XLATE_DIRS = {
+ "Southwest", "West", "Northwest", "North", "Northeast",
+ "East", "Southeast", "South"
+ };
+
+ /** Default frame rate. */
+ public static final int DEFAULT_FRAME_RATE = 15;
+
+ /** Animation mode indicating no animation. */
+ public static final int NO_ANIMATION = 0;
+
+ /** Animation mode indicating movement cued animation. */
+ public static final int MOVEMENT_CUED = 1;
+
+ /** Animation mode indicating time based animation. */
+ public static final int TIME_BASED = 2;
+
/**
* Construct a sprite object.
*
@@ -82,15 +113,6 @@ public class Sprite
return _rbounds;
}
- /**
- * Return whether the sprite is currently moving (following an
- * assigned path.)
- */
- public boolean isMoving ()
- {
- return (_dest != null);
- }
-
/**
* Moves the sprite to the specified location. The location specified
* will be used as the center of the bottom edge of the sprite.
@@ -112,6 +134,18 @@ public class Sprite
invalidate();
}
+ /**
+ * Sprites have an orientation in one of the eight cardinal
+ * directions: DIR_NORTH, DIR_NORTHEAST,
+ * etc. Sprite derived classes can choose to override this member
+ * function and select a different set of images based on their
+ * orientation, or they can ignore the orientation information.
+ */
+ public void setOrientation (int orient)
+ {
+ _orient = orient;
+ }
+
/**
* Initialize the sprite object with its variegated parameters.
*/
@@ -122,15 +156,10 @@ public class Sprite
updateRenderOrigin();
- // set default velocity and units
- _vel = new Point(1, 1);
- _velUnit = new Point(1, 1);
- updateVelocity();
-
// initialize frame animation member data
_frameIdx = 0;
- _animDelay = ANIM_NONE;
- _numTicks = 0;
+ _animMode = NO_ANIMATION;
+ _frameDelay = 1000L/DEFAULT_FRAME_RATE;
setFrames(frames);
invalidate();
@@ -164,17 +193,9 @@ public class Sprite
*/
public void paintPath (Graphics2D gfx)
{
- if (_fullpath == null) return;
-
- gfx.setColor(Color.red);
- Point prev = null;
- int size = _fullpath.size();
- for (int ii = 0; ii < size; ii++) {
- PathNode n = (PathNode)_fullpath.getNode(ii);
- if (prev == null) prev = n.loc;
- gfx.drawLine(prev.x, prev.y, n.loc.x, n.loc.y);
- prev = n.loc;
- }
+ if (_path != null) {
+ _path.paint(gfx);
+ }
}
/**
@@ -204,14 +225,33 @@ public class Sprite
}
/**
- * Set the number of ticks to wait before switching to the next image
- * in the array of images used to display the sprite.
+ * Sets the animation mode for this sprite. The available modes are:
*
- * @param ticks the number of ticks.
+ *
TIME_BASED: cues the animation based on a target
+ * frame rate (specified via {@link #setFrameRate}).
+ * MOVEMENT_CUED: ticks the animation to the next
+ * frame every time the sprite is moved along its path.
+ * NO_ANIMATION: disables animation.
+ * TIME_BASED.
+ *
+ * @param fps the desired frames per second.
+ */
+ public void setFrameRate (int fps)
+ {
+ _frameDelay = 1000L/fps;
}
/**
@@ -228,6 +268,7 @@ public class Sprite
}
_frames = frames;
+ _frameIdx %= _frames.getFrameCount();
_frame = _frames.getFrame(_frameIdx);
// determine our drawing offsets and rendered rectangle size
@@ -248,53 +289,6 @@ public class Sprite
invalidate();
}
- /**
- * Set the sprite's velocity when following a path.
- *
- * @param vx the x-axis velocity.
- * @param vy the y-axis velocity.
- */
- public void setVelocity (int vx, int vy)
- {
- _vel.setLocation(vx, vy);
- updateVelocity();
- }
-
- /**
- * Set the units upon which the sprite's velocity is based.
- *
- * @param ux the x-axis units.
- * @param uy the y-axis units.
- */
- public void setVelocityUnits (int ux, int uy)
- {
- _velUnit.setLocation(ux, uy);
- updateVelocity();
- }
-
- /**
- * Update the distance the sprite moves each tick, based on the
- * sprite velocity and the velocity units.
- */
- protected void updateVelocity ()
- {
- _fracx = (_vel.x / (float)_velUnit.x);
- _fracy = (_vel.y / (float)_velUnit.y);
- }
-
- /**
- * Stop the sprite from any movement along a path it may be
- * engaged in.
- */
- public void stop ()
- {
- // TODO: make sure we come to a stop on a full coordinate,
- // even in the case where we aborted a path mid-traversal.
- _dest = null;
- _path = null;
- _fullpath = null;
- }
-
/**
* Set the sprite's active path and start moving it along its
* merry way. If the sprite is already moving along a previous
@@ -305,64 +299,34 @@ public class Sprite
*/
public void move (Path path)
{
- // make sure following the path is a sensible thing to do
- if (path == null || path.size() < 2) {
- // halt any previously existing movement since, regardless
- // of its reasonableness, we've been asked to follow a new
- // path
- stop();
- return;
- }
+ // save our path
+ _path = path;
- // save an enumeration of the path nodes
- _path = path.elements();
-
- // and the full path for potential rendering
- _fullpath = path;
-
- // skip the first node since it's our starting position.
- // perhaps someday we'll do something with this.
- _path.next();
-
- // start our meandering
- moveAlongPath();
+ // and initialize it
+ _path.init(this, System.currentTimeMillis());
}
/**
- * Start the sprite moving toward the next node in its path.
+ * Cancels any path that the sprite may currently be moving along.
*/
- protected void moveAlongPath ()
+ public void cancelMove ()
{
- if (!_path.hasNext()) {
- // inform observers that we've finished our path
- notifyObservers(SpriteEvent.FINISHED_PATH, _path);
- // clear out our path and bail
- stop();
- return;
- }
+ // TODO: make sure we come to a stop on a full coordinate,
+ // even in the case where we aborted a path mid-traversal.
- if (_dest != null) {
- // inform observers that we've finished a path node
- notifyObservers(SpriteEvent.FINISHED_PATH_NODE, _dest);
- }
+ _path = null;
+ }
- // grab the next node in our path
- _dest = (PathNode)_path.next();
+ /**
+ * Called by the active path when it has completed.
+ */
+ protected void pathCompleted ()
+ {
+ // inform observers that we've finished our path
+ notifyObservers(SpriteEvent.FINISHED_PATH, _path);
- // if we're already here, move on to the next node
- if (_x == _dest.loc.x && _y == _dest.loc.y) {
- moveAlongPath();
- return;
- }
-
- // determine the horizontal/vertical move increments
- float dist = MathUtil.distance(_x, _y, _dest.loc.x, _dest.loc.y);
- _incx = (float)(_dest.loc.x - _x) / (dist / _fracx);
- _incy = (float)(_dest.loc.y - _y) / (dist / _fracy);
-
- // init position data used to track fractional pixels
- _movex = _x;
- _movey = _y;
+ // we no longer want to keep a reference to this path
+ _path = null;
}
/**
@@ -379,7 +343,9 @@ public class Sprite
*/
public void invalidate ()
{
- if (_frame == null) return;
+ if (_frame == null) {
+ return;
+ }
if (_spritemgr != null) {
_spritemgr.addDirtyRect(getRenderedBounds());
@@ -391,59 +357,49 @@ public class Sprite
}
/**
- * This method is called periodically by the SpriteManager to give
- * the sprite a chance to update its state.
+ * This method is called periodically by the sprite manager to give
+ * the sprite a chance to update its state. The sprite manager will
+ * attempt to call this with the desired refresh rate, but will drop
+ * calls to tick if it can't keep up. Thus, a sprite should rely on
+ * the timestamp information to compute elapsed progress if it wishes
+ * to handle heavy loads gracefully.
*/
- public void tick ()
+ public void tick (long timestamp)
{
- // increment the display image if performing image animation
- if (_animDelay != ANIM_NONE && (_numTicks++ == _animDelay)) {
- _numTicks = 0;
- _frameIdx = (_frameIdx + 1) % _frames.getFrameCount();
- _frame = _frames.getFrame(_frameIdx);
+ int fcount = _frames.getFrameCount();
+ int nfidx = _frameIdx;
+ boolean moved = false;
+ // move the sprite along toward its destination, if any
+ if (_path != null) {
+ moved = _path.updatePosition(this, timestamp);
+ }
+
+ // increment the display image if performing image animation
+ switch (_animMode) {
+ case NO_ANIMATION:
+ // nothing doing
+ break;
+
+ case TIME_BASED:
+ _frameIdx = (int)((timestamp/_frameDelay) % fcount);
+ break;
+
+ case MOVEMENT_CUED:
+ // update the frame if the sprite moved
+ if (moved) {
+ nfidx = (_frameIdx + 1) % fcount;
+ }
+ break;
+ }
+
+ // only update the sprite if our frame index changed
+ if (nfidx != _frameIdx) {
+ _frameIdx = nfidx;
+ _frame = _frames.getFrame(_frameIdx);
// dirty our rectangle since we've altered our display image
invalidate();
}
-
- // move the sprite along toward its destination, if any
- if (_dest != null) {
- handleMove();
- }
- }
-
- /**
- * Actually move the sprite's position toward its destination one
- * display increment.
- */
- protected void handleMove ()
- {
- // dirty our rectangle since we're going to move
- invalidate();
-
- // move the sprite incrementally toward its goal
- _x = (int)(_movex += _incx);
- _y = (int)(_movey += _incy);
-
- // stop moving once we've reached our destination
- if (_incx > 0 && _x > _dest.loc.x ||
- _incx < 0 && _x < _dest.loc.x ||
- _incy > 0 && _y > _dest.loc.y ||
- _incy < 0 && _y < _dest.loc.y) {
-
- // make sure we stop exactly where desired
- _x = _dest.loc.x;
- _y = _dest.loc.y;
-
- // move further along the path if necessary
- moveAlongPath();
- }
-
- // update the draw coordinates to reflect our new position
- updateRenderOrigin();
-
- // dirty our rectangle in the new position
- invalidate();
}
/**
@@ -490,7 +446,9 @@ public class Sprite
*/
protected void notifyObservers (int eventCode, Object arg)
{
- if (_observers == null) return;
+ if (_observers == null) {
+ return;
+ }
SpriteEvent evt = new SpriteEvent(this, eventCode, arg);
_spritemgr.notifySpriteObservers(_observers, evt);
@@ -508,9 +466,6 @@ public class Sprite
return buf.append("]").toString();
}
- /** Value used to denote that no image animation is desired. */
- protected static final int ANIM_NONE = -1;
-
/** The images used to render the sprite. */
protected MultiFrameImage _frames;
@@ -520,6 +475,9 @@ public class Sprite
/** The current frame index to render. */
protected int _frameIdx;
+ /** The orientation of this sprite. */
+ protected int _orient = DIR_NONE;
+
/** The location of the sprite in pixel coordinates. */
protected int _x, _y;
@@ -529,35 +487,14 @@ public class Sprite
/** Our rendered bounds in pixel coordinates. */
protected Rectangle _rbounds = new Rectangle();
- /** The PathNode objects describing the path the sprite is following. */
- protected Iterator _path;
+ /** What type of animation is desired for this sprite. */
+ protected int _animMode;
- /** When moving, the sprite's destination path node. */
- protected PathNode _dest;
+ /** For how many milliseconds to display an animation frame. */
+ protected long _frameDelay;
- /** When moving, the sprite position including fractional pixels. */
- protected float _movex, _movey;
-
- /** When moving, the distance to move on each axis per tick. */
- protected float _incx, _incy;
-
- /** The distance to move on the straight path line per tick. */
- protected float _fracx, _fracy;
-
- /** The number of ticks to wait before rendering with the next image. */
- protected int _animDelay;
-
- /** The number of ticks since the last image animation. */
- protected int _numTicks;
-
- /** When moving, the full path the sprite is traversing. */
- protected Path _fullpath;
-
- /** The sprite velocity when moving. */
- protected Point _vel;
-
- /** The sprite velocity units. */
- protected Point _velUnit;
+ /** When moving, the path the sprite is traversing. */
+ protected Path _path;
/** The sprite observers observing this sprite. */
protected ArrayList _observers;
diff --git a/src/java/com/threerings/media/sprite/SpriteManager.java b/src/java/com/threerings/media/sprite/SpriteManager.java
index ed47f4220..f4e576515 100644
--- a/src/java/com/threerings/media/sprite/SpriteManager.java
+++ b/src/java/com/threerings/media/sprite/SpriteManager.java
@@ -1,5 +1,5 @@
//
-// $Id: SpriteManager.java,v 1.12 2001/09/07 23:01:53 shaper Exp $
+// $Id: SpriteManager.java,v 1.13 2001/09/13 19:10:26 mdb Exp $
package com.threerings.media.sprite;
@@ -154,10 +154,10 @@ public class SpriteManager
* queue by the {@link AnimationManager}. Handles moving about of
* sprites and reporting of sprite collisions.
*/
- public void tick ()
+ public void tick (long timestamp)
{
// tick all sprites
- tickSprites();
+ tickSprites(timestamp);
// re-sort the sprite list to account for potential new positions
sortSprites();
@@ -179,12 +179,12 @@ public class SpriteManager
* chance to move themselves about, change their display image,
* and so forth.
*/
- protected void tickSprites ()
+ protected void tickSprites (long timestamp)
{
int size = _sprites.size();
for (int ii = 0; ii < size; ii++) {
Sprite sprite = (Sprite)_sprites.get(ii);
- sprite.tick();
+ sprite.tick(timestamp);
}
}
diff --git a/src/java/com/threerings/media/util/LineSegmentPath.java b/src/java/com/threerings/media/util/LineSegmentPath.java
index 76e1c0367..76c560532 100644
--- a/src/java/com/threerings/media/util/LineSegmentPath.java
+++ b/src/java/com/threerings/media/util/LineSegmentPath.java
@@ -1,65 +1,50 @@
//
-// $Id: LineSegmentPath.java,v 1.8 2001/09/05 00:40:17 shaper Exp $
+// $Id: LineSegmentPath.java,v 1.9 2001/09/13 19:10:26 mdb Exp $
package com.threerings.media.sprite;
+import java.awt.Color;
+import java.awt.Point;
+import java.awt.Graphics2D;
+
import java.util.ArrayList;
import java.util.Iterator;
import com.samskivert.util.StringUtil;
+import com.threerings.media.util.MathUtil;
/**
- * The Path class represents the path a sprite follows
- * while meandering about the screen. There must be at least two
- * nodes in any worthwhile path. The direction of the first node in
- * the path is meaningless since the sprite begins at that node and
- * will therefore never be heading towards it.
+ * The LineSegmentPath class is used to cause a sprite to
+ * follow a path that is made up of a sequence of line segments. There
+ * must be at least two nodes in any worthwhile path. The direction of the
+ * first node in the path is meaningless since the sprite begins at that
+ * node and will therefore never be heading towards it.
*/
-public class Path
+public class LineSegmentPath implements Path
{
- /** The number of distinct directions. */
- public static final int NUM_DIRECTIONS = 8;
-
- /** Direction constants. */
- public static final int DIR_NONE = -1;
- public static final int DIR_SOUTHWEST = 0;
- public static final int DIR_WEST = 1;
- public static final int DIR_NORTHWEST = 2;
- public static final int DIR_NORTH = 3;
- public static final int DIR_NORTHEAST = 4;
- public static final int DIR_EAST = 5;
- public static final int DIR_SOUTHEAST = 6;
- public static final int DIR_SOUTH = 7;
-
- /** String translations for the direction constants. */
- public static String[] XLATE_DIRS = {
- "Southwest", "West", "Northwest", "North", "Northeast",
- "East", "Southeast", "South"
- };
-
/**
- * Construct a Path object.
+ * Construct a LineSegmentPath object.
*/
- public Path ()
+ public LineSegmentPath ()
{
_nodes = new ArrayList();
}
/**
- * Construct a Path object with the specified
- * starting node coordinates. An arbitrary direction will be
- * assigned to the starting node.
+ * Construct a LineSegmentPath object with the specified
+ * starting node coordinates. An arbitrary direction will be assigned
+ * to the starting node.
*
* @param x the starting node x-position.
* @param y the starting node y-position.
*/
- public Path (int x, int y)
+ public LineSegmentPath (int x, int y)
{
_nodes = new ArrayList();
// add the starting node with an arbitrarily chosen direction
// since direction is meaningless here
- addNode(x, y, DIR_NORTH);
+ addNode(x, y, Sprite.DIR_NORTH);
}
/**
@@ -76,15 +61,15 @@ public class Path
}
/**
- * Add a node to the path with the specified destination point.
- * An arbitrary direction will be assigned to the node.
+ * Add a node to the path with the specified destination point. An
+ * arbitrary direction will be assigned to the node.
*
* @param x the x-position.
* @param y the y-position.
*/
public void addNode (int x, int y)
{
- _nodes.add(new PathNode(x, y, Path.DIR_NORTH));
+ _nodes.add(new PathNode(x, y, Sprite.DIR_NORTH));
}
/**
@@ -100,14 +85,6 @@ public class Path
return (PathNode)_nodes.get(idx);
}
- /**
- * Return an enumeration of the PathNode objects in this path.
- */
- public Iterator elements ()
- {
- return _nodes.iterator();
- }
-
/**
* Return the number of nodes in the path.
*/
@@ -116,6 +93,127 @@ public class Path
return _nodes.size();
}
+ /**
+ * Sets the velocity of this sprite in pixels per millisecond. The
+ * velocity is measured as pixels traversed along the path that the
+ * sprite is traveling rather than in the x or y directions
+ * individually.
+ *
+ * @param velocity the sprite velocity in pixels per millisecond.
+ */
+ public void setVelocity (float velocity)
+ {
+ _vel = velocity;
+ }
+
+ // documentation inherited
+ public void init (Sprite sprite, long timestamp)
+ {
+ // if we have only one node then let the sprite know that we're
+ // done straight away
+ if (size() < 2) {
+ // move the sprite to the location specified by the first node
+ // (assuming we have a first node)
+ if (size() == 1) {
+ PathNode node = (PathNode)_nodes.get(0);
+ sprite.setLocation(node.loc.x, node.loc.y);
+ }
+ // and let the sprite know that we're done
+ sprite.pathCompleted();
+ return;
+ }
+
+ // and an enumeration of the path nodes
+ _niter = _nodes.iterator();
+
+ // pretend like we were previously heading to our starting position
+ _dest = (PathNode)_niter.next();
+
+ // begin traversing the path
+ headToNextNode(sprite, timestamp, timestamp);
+ }
+
+ // documentation inherited
+ public boolean updatePosition (Sprite sprite, long timestamp)
+ {
+ // figure out how far along this segment we should be
+ long msecs = timestamp - _nodestamp;
+ float travpix = msecs * _vel;
+ float pctdone = travpix / _seglength;
+
+ // if we've moved beyond the end of the path, we need to adjust
+ // the timestamp to determine how much time we used getting to the
+ // end of this node, then move to the next one
+ if (pctdone >= 1.0) {
+ long used = (long)(_seglength / _vel);
+ return headToNextNode(sprite, _nodestamp + used, timestamp);
+ }
+
+ // otherwise we position the sprite along the path
+ int ox = sprite.getX();
+ int oy = sprite.getY();
+ int nx = _src.loc.x + (int)((_dest.loc.x - _src.loc.x) * pctdone);
+ int ny = _src.loc.y + (int)((_dest.loc.y - _src.loc.y) * pctdone);
+
+ // only update the sprite's location if it actually moved
+ if (ox != nx || oy != ny) {
+ sprite.setLocation(nx, ny);
+ return true;
+ }
+
+ return false;
+ }
+
+ protected boolean headToNextNode (Sprite sprite, long startstamp, long now)
+ {
+ // check to see if we've completed our path
+ if (!_niter.hasNext()) {
+ // move the sprite to the location of our last destination
+ sprite.setLocation(_dest.loc.x, _dest.loc.y);
+ sprite.pathCompleted();
+ return true;
+ }
+
+ // our previous destination is now our source
+ _src = _dest;
+
+ // pop the next node off the path
+ _dest = (PathNode)_niter.next();
+
+ // adjust the sprite's orientation
+ sprite.setOrientation(_dest.dir);
+
+ // make a note of when we started traversing this node
+ _nodestamp = startstamp;
+
+ // figure out the distance from source to destination
+ _seglength = MathUtil.distance(_src.loc.x, _src.loc.y,
+ _dest.loc.x, _dest.loc.y);
+
+ // if we're already there (the segment length is zero), we skip to
+ // the next segment
+ if (_seglength == 0) {
+ return headToNextNode(sprite, startstamp, now);
+ }
+
+ // now update the sprite's position based on our progress thus far
+ return updatePosition(sprite, now);
+ }
+
+ public void paint (Graphics2D gfx)
+ {
+ gfx.setColor(Color.red);
+ Point prev = null;
+ int size = size();
+ for (int ii = 0; ii < size; ii++) {
+ PathNode n = (PathNode)getNode(ii);
+ if (prev != null) {
+ gfx.drawLine(prev.x, prev.y, n.loc.x, n.loc.y);
+ }
+ prev = n.loc;
+ }
+ }
+
public String toString ()
{
return StringUtil.toString(_nodes.iterator());
@@ -123,4 +221,34 @@ public class Path
/** The nodes that make up the path. */
protected ArrayList _nodes;
+
+ /** We use this when moving along this path. */
+ protected Iterator _niter;
+
+ /** When moving, the sprite's source path node. */
+ protected PathNode _src;
+
+ /** When moving, the sprite's destination path node. */
+ protected PathNode _dest;
+
+ /** The time at which we started traversing the current node. */
+ protected long _nodestamp;
+
+ /** The length in pixels of the current path segment. */
+ protected float _seglength;
+
+ /** The path velocity in pixels per millisecond. */
+ protected float _vel = DEFAULT_VELOCITY;
+
+ /** When moving, the sprite position including fractional pixels. */
+ protected float _movex, _movey;
+
+ /** When moving, the distance to move on each axis per tick. */
+ protected float _incx, _incy;
+
+ /** The distance to move on the straight path line per tick. */
+ protected float _fracx, _fracy;
+
+ /** Default sprite velocity. */
+ protected static final float DEFAULT_VELOCITY = 200f/1000f;
}
diff --git a/src/java/com/threerings/media/util/Path.java b/src/java/com/threerings/media/util/Path.java
new file mode 100644
index 000000000..0594199a4
--- /dev/null
+++ b/src/java/com/threerings/media/util/Path.java
@@ -0,0 +1,45 @@
+//
+// $Id: Path.java,v 1.1 2001/09/13 19:10:26 mdb Exp $
+
+package com.threerings.media.sprite;
+
+import java.awt.Graphics2D;
+
+/**
+ * A path is used to cause a sprite to follow a particular path along the
+ * screen. The sprite will call into the path every time it is ticked by
+ * the animation manager and the path is responsible for updating the
+ * position of the sprite based on the time that has elapsed since the
+ * sprite started down the path. The animation manager attempts to tick
+ * once per frame at the desired frame rate, but may tick less often if
+ * CPU resources are limited, thus paths should not rely on tick counts
+ * but instead use elapsed time to determine progress.
+ *
+ * The path should call back to the sprite (via {@link
+ * Sprite#pathCompleted}) to let it know when the path has been completed.
+ */
+public interface Path
+{
+ /**
+ * Called once to let the path prepare itself for the process of
+ * animating the supplied sprite.
+ */
+ public void init (Sprite sprite, long timestamp);
+
+ /**
+ * Called to request that this path update the position of the
+ * specified sprite based on the supplied timestamp information. A
+ * path should record its initial timestamp and determine the progress
+ * of the sprite along the path based on the time elapsed since the
+ * sprite began down the path.
+ *
+ * @return true if the sprite's position was updated, false if the
+ * path determined that the sprite should not move at this time.
+ */
+ public boolean updatePosition (Sprite sprite, long timestamp);
+
+ /**
+ * Paint this path on the screen (used for debugging purposes only).
+ */
+ public void paint (Graphics2D gfx);
+}
diff --git a/src/java/com/threerings/miso/client/IsoSceneView.java b/src/java/com/threerings/miso/client/IsoSceneView.java
index 71936cbf9..0788bb97b 100644
--- a/src/java/com/threerings/miso/client/IsoSceneView.java
+++ b/src/java/com/threerings/miso/client/IsoSceneView.java
@@ -1,5 +1,5 @@
//
-// $Id: IsoSceneView.java,v 1.53 2001/08/29 19:50:46 shaper Exp $
+// $Id: IsoSceneView.java,v 1.54 2001/09/13 19:10:26 mdb Exp $
package com.threerings.miso.scene;
@@ -618,7 +618,8 @@ public class IsoSceneView implements EditableSceneView
// to second tile, and penultimate to ultimate tile.
// construct path with starting screen position
- Path path = new Path(sprite.getX(), sprite.getY());
+ LineSegmentPath path =
+ new LineSegmentPath(sprite.getX(), sprite.getY());
// add all nodes on the calculated path
Point nspos = new Point();
diff --git a/src/java/com/threerings/miso/client/Location.java b/src/java/com/threerings/miso/client/Location.java
index 8765e5511..2b3206a6c 100644
--- a/src/java/com/threerings/miso/client/Location.java
+++ b/src/java/com/threerings/miso/client/Location.java
@@ -1,9 +1,9 @@
//
-// $Id: Location.java,v 1.5 2001/08/14 23:35:22 mdb Exp $
+// $Id: Location.java,v 1.6 2001/09/13 19:10:26 mdb Exp $
package com.threerings.miso.scene;
-import com.threerings.media.sprite.Path;
+import com.threerings.media.sprite.Sprite;
/**
* The Location class represents a unique well-defined
@@ -45,7 +45,7 @@ public class Location
{
this.x = x;
this.y = y;
- this.orient = Path.DIR_SOUTHWEST;
+ this.orient = Sprite.DIR_SOUTHWEST;
}
/**
diff --git a/src/java/com/threerings/miso/client/util/IsoUtil.java b/src/java/com/threerings/miso/client/util/IsoUtil.java
index 4a4acaecb..cf5b45d8f 100644
--- a/src/java/com/threerings/miso/client/util/IsoUtil.java
+++ b/src/java/com/threerings/miso/client/util/IsoUtil.java
@@ -1,12 +1,12 @@
//
-// $Id: IsoUtil.java,v 1.5 2001/08/15 22:16:43 shaper Exp $
+// $Id: IsoUtil.java,v 1.6 2001/09/13 19:10:26 mdb Exp $
package com.threerings.miso.scene.util;
import java.awt.Point;
import java.awt.Polygon;
-import com.threerings.media.sprite.Path;
+import com.threerings.media.sprite.Sprite;
import com.threerings.media.util.MathUtil;
import com.threerings.miso.Log;
@@ -28,7 +28,7 @@ public class IsoUtil
* @param bx the x-position of point B.
* @param by the y-position of point B.
*
- * @return the direction specified as one of the Path
+ * @return the direction specified as one of the Sprite
* class's direction constants.
*/
public static int getDirection (
@@ -50,7 +50,7 @@ public class IsoUtil
// compare tile coordinates to determine direction
int dir = getIsoDirection(tax, tay, tbx, tby);
- if (dir != Path.DIR_NONE) return dir;
+ if (dir != Sprite.DIR_NONE) return dir;
// destination point is in the same tile as the
// origination point, so consider fine coordinates
@@ -66,7 +66,7 @@ public class IsoUtil
dir = getIsoDirection(fax, fay, fbx, fby);
// arbitrarily return southwest if fine coords were also equivalent
- return (dir == -1) ? Path.DIR_SOUTHWEST : dir;
+ return (dir == -1) ? Sprite.DIR_SOUTHWEST : dir;
}
/**
@@ -81,23 +81,23 @@ public class IsoUtil
* @param bx the x-position of point B.
* @param by the y-position of point B.
*
- * @return the direction specified as one of the Path
- * class's direction constants, or Path.DIR_NONE
+ * @return the direction specified as one of the Sprite
+ * class's direction constants, or Sprite.DIR_NONE
* if point B is equivalent to point A.
*/
public static int getIsoDirection (int ax, int ay, int bx, int by)
{
if (bx > ax) {
- if (by == ay) return Path.DIR_SOUTH;
- return (by < ay) ? Path.DIR_SOUTHEAST : Path.DIR_SOUTHWEST;
+ if (by == ay) return Sprite.DIR_SOUTH;
+ return (by < ay) ? Sprite.DIR_SOUTHEAST : Sprite.DIR_SOUTHWEST;
} else if (bx == ax) {
- if (by == ay) return Path.DIR_NONE;
- return (by < ay) ? Path.DIR_EAST : Path.DIR_WEST;
+ if (by == ay) return Sprite.DIR_NONE;
+ return (by < ay) ? Sprite.DIR_EAST : Sprite.DIR_WEST;
} else { // bx < ax
- if (by == ay) return Path.DIR_NORTH;
- return (by < ay) ? Path.DIR_NORTHEAST : Path.DIR_NORTHWEST;
+ if (by == ay) return Sprite.DIR_NORTH;
+ return (by < ay) ? Sprite.DIR_NORTHEAST : Sprite.DIR_NORTHWEST;
}
}
diff --git a/src/java/com/threerings/miso/tile/TileUtil.java b/src/java/com/threerings/miso/tile/TileUtil.java
index 11a6fddb8..3783f8db2 100644
--- a/src/java/com/threerings/miso/tile/TileUtil.java
+++ b/src/java/com/threerings/miso/tile/TileUtil.java
@@ -1,12 +1,12 @@
//
-// $Id: TileUtil.java,v 1.4 2001/09/05 00:44:37 shaper Exp $
+// $Id: TileUtil.java,v 1.5 2001/09/13 19:10:26 mdb Exp $
package com.threerings.miso.tile;
import java.awt.Image;
import com.threerings.media.sprite.MultiFrameImage;
-import com.threerings.media.sprite.Path;
+import com.threerings.media.sprite.Sprite;
import com.threerings.media.tile.*;
import com.threerings.miso.scene.AmbulatorySprite;
@@ -20,7 +20,7 @@ public class TileUtil
* Returns an array of multi-frame images corresponding to the
* frames of animation used to render the ambulatory sprite in
* each of the directions it may face. The tileset id referenced
- * must contain Path.NUM_DIRECTIONS rows of tiles,
+ * must contain Sprite.NUM_DIRECTIONS rows of tiles,
* with each row containing NUM_DIR_FRAMES tiles.
*
* @param tilemgr the tile manager to retrieve tiles from.
@@ -31,9 +31,9 @@ public class TileUtil
public static MultiFrameImage[] getAmbulatorySpriteFrames (
TileManager tilemgr, int tsid)
{
- MultiFrameImage[] anims = new MultiFrameImage[Path.NUM_DIRECTIONS];
+ MultiFrameImage[] anims = new MultiFrameImage[Sprite.NUM_DIRECTIONS];
- for (int ii = 0; ii < Path.NUM_DIRECTIONS; ii++) {
+ for (int ii = 0; ii < Sprite.NUM_DIRECTIONS; ii++) {
Tile[] tiles = new Tile[NUM_DIR_FRAMES];
for (int jj = 0; jj < NUM_DIR_FRAMES; jj++) {
int idx = (ii * NUM_DIR_FRAMES) + jj;
diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java b/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java
index 1f20d00bb..50d63e835 100644
--- a/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java
+++ b/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java
@@ -1,5 +1,5 @@
//
-// $Id: ViewerFrame.java,v 1.17 2001/09/05 00:45:27 shaper Exp $
+// $Id: ViewerFrame.java,v 1.18 2001/09/13 19:10:26 mdb Exp $
package com.threerings.miso.viewer;
@@ -46,7 +46,6 @@ class ViewerFrame extends JFrame implements WindowListener
MultiFrameImage[] anims =
TileUtil.getAmbulatorySpriteFrames(tilemgr, TSID_CHAR);
AmbulatorySprite sprite = new AmbulatorySprite(300, 300, anims);
- sprite.setVelocity(6, 6);
spritemgr.addSprite(sprite);
// create a top-level panel to manage everything