From 780e39d4e97bacf2d4d29e44dd4476a2cbb2c390 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 13 Sep 2001 19:10:26 +0000 Subject: [PATCH] Modified animation manager to provide a timestamp with every tick. Modified sprite and path code to make use of these timestamps to determine a sprite's progress along the path. Abstracted the path stuff so that paths other than line segment paths can be cleanly implemented. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@337 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../com/threerings/cast/CharacterSprite.java | 46 +-- .../media/sprite/AnimationManager.java | 7 +- .../com/threerings/media/sprite/Sprite.java | 355 +++++++----------- .../media/sprite/SpriteManager.java | 10 +- .../media/util/LineSegmentPath.java | 218 ++++++++--- src/java/com/threerings/media/util/Path.java | 45 +++ .../threerings/miso/client/IsoSceneView.java | 5 +- .../com/threerings/miso/client/Location.java | 6 +- .../threerings/miso/client/util/IsoUtil.java | 26 +- .../com/threerings/miso/tile/TileUtil.java | 10 +- .../threerings/miso/viewer/ViewerFrame.java | 3 +- 11 files changed, 410 insertions(+), 321 deletions(-) create mode 100644 src/java/com/threerings/media/util/Path.java 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. + * + * + * @param mode the desired animation mode. */ - public void setAnimationDelay (int ticks) + public void setAnimationMode (int mode) { - _animDelay = ticks; + _animMode = mode; + } + + /** + * Sets the number of frames per second desired for the sprite + * animation. This is only used when the animation mode is + * 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