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
This commit is contained in:
@@ -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 <code>Path</code> 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 <code>LineSegmentPath</code> 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 <code>Path</code> object.
|
||||
* Construct a <code>LineSegmentPath</code> object.
|
||||
*/
|
||||
public Path ()
|
||||
public LineSegmentPath ()
|
||||
{
|
||||
_nodes = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a <code>Path</code> object with the specified
|
||||
* starting node coordinates. An arbitrary direction will be
|
||||
* assigned to the starting node.
|
||||
* Construct a <code>LineSegmentPath</code> 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user