Moved much of the IsoSceneView data into the IsoSceneModel. More work

on sprites and animation.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@138 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Walter Korman
2001-08-02 00:42:02 +00:00
parent ad506a13d5
commit 1032b8f325
14 changed files with 795 additions and 177 deletions
@@ -1,47 +1,67 @@
//
// $Id: AnimationManager.java,v 1.2 2001/07/31 01:38:28 shaper Exp $
// $Id: AnimationManager.java,v 1.3 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.sprite;
import java.awt.Component;
import java.util.ArrayList;
import com.samskivert.util.Interval;
import com.samskivert.util.IntervalManager;
import com.threerings.miso.Log;
import com.threerings.miso.scene.SceneView;
import com.threerings.miso.util.PerformanceMonitor;
import com.threerings.miso.util.PerformanceObserver;
/**
* The AnimationManager handles the regular refreshing of the scene
* view to allow for animation. It also may someday manage special
* scene-wide animations, such as rain, fog, or earthquakes.
*/
public class AnimationManager
public class AnimationManager implements Interval, PerformanceObserver
{
/**
* Construct and initialize the animation manager with a sprite
* manager and the panel that animations will take place within.
*/
public AnimationManager (SpriteManager spritemgr, Component target)
public AnimationManager (SpriteManager spritemgr, Component target,
SceneView view)
{
// save off references to the objects we care about
_spritemgr = spritemgr;
_target = target;
_view = view;
// create the interval for refreshing the display
Interval refresher = new Interval() {
public void intervalExpired (int id, Object arg)
{
// refresh the display
_target.repaint();
// register to monitor the refresh action
PerformanceMonitor.register(this, "refresh", 1000);
// call tick on all sprites
_spritemgr.tick();
}
};
// register ourselves with the interval manager
IntervalManager.register(this, REFRESH_INTERVAL, null, true);
}
// register ourselves with the interval mgr
IntervalManager.register(refresher, REFRESH_INTERVAL, null, true);
public void intervalExpired (int id, Object arg)
{
// refresh the display
_target.repaint();
// call tick on all sprites
_spritemgr.tick();
// invalidate screen-rects dirtied by sprites
ArrayList rects = _spritemgr.getDirtyRects();
_view.invalidateRects(rects);
// update frame-rate information
//PerformanceMonitor.tick(this, "refresh");
}
public void checkpoint (String name, int ticks)
{
Log.info(name + "[ticks=" + ticks + "].");
}
/** The desired number of refresh operations per second. */
protected static final int FRAME_RATE = 60;
protected static final int FRAME_RATE = 20;
/** The milliseconds to sleep to obtain desired frame rate. */
protected static final long REFRESH_INTERVAL = 1000 / FRAME_RATE;
@@ -51,4 +71,7 @@ public class AnimationManager
/** The component to refresh. */
protected Component _target;
/** The scene view. */
protected SceneView _view;
}
@@ -1,5 +1,5 @@
//
// $Id: MobileSprite.java,v 1.2 2001/07/31 01:38:28 shaper Exp $
// $Id: MobileSprite.java,v 1.3 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.sprite;
@@ -24,11 +24,14 @@ public class MobileSprite extends Sprite
* @param tilemgr the tile manager to retrieve tiles from.
* @param tsid the tileset id containing the sprite tiles.
*/
public MobileSprite (int x, int y, TileManager tilemgr, int tsid)
public MobileSprite (SpriteManager spritemgr, int x, int y,
TileManager tilemgr, int tsid)
{
super(x, y);
super(spritemgr, x, y);
_charTiles = getTiles(tilemgr, tsid);
_dir = DIR_SOUTH;
_dir = Path.DIR_SOUTH;
setTiles(_charTiles[0]);
}
@@ -36,7 +39,7 @@ public class MobileSprite extends Sprite
* Returns a two-dimensional array of tiles corresponding to the
* frames of animation used to render the mobile sprite in each of
* the directions it may face. The tileset id referenced must
* contain <code>NUM_DIRECTIONS</code> rows of tiles, with each
* contain <code>Path.NUM_DIRECTIONS</code> rows of tiles, with each
* row containing <code>NUM_DIR_FRAMES</code> tiles.
*
* @param tilemgr the tile manager to retrieve tiles from.
@@ -47,9 +50,9 @@ public class MobileSprite extends Sprite
protected Tile[][] getTiles (TileManager tilemgr, int tsid)
{
Tile[][] tiles =
new Tile[NUM_DIRECTIONS][NUM_DIR_FRAMES];
new Tile[Path.NUM_DIRECTIONS][NUM_DIR_FRAMES];
for (int ii = 0; ii < NUM_DIRECTIONS; ii++) {
for (int ii = 0; ii < Path.NUM_DIRECTIONS; ii++) {
for (int jj = 0; jj < NUM_DIR_FRAMES; jj++) {
int idx = (ii * NUM_DIR_FRAMES) + jj;
tiles[ii][jj] = tilemgr.getTile(tsid, idx);
@@ -87,32 +90,19 @@ public class MobileSprite extends Sprite
protected int getDirection (int x, int y)
{
if (x >= this.x - DIR_BUFFER && x <= this.x + DIR_BUFFER) {
return (y < this.y) ? DIR_NORTH : DIR_SOUTH;
return (y < this.y) ? Path.DIR_NORTH : Path.DIR_SOUTH;
} else if (y >= this.y - DIR_BUFFER && y <= this.y + DIR_BUFFER) {
return (x >= this.x) ? DIR_EAST : DIR_WEST;
return (x >= this.x) ? Path.DIR_EAST : Path.DIR_WEST;
} else if (x > this.x) {
return (y < this.y) ? DIR_NORTHEAST : DIR_SOUTHEAST;
return (y < this.y) ? Path.DIR_NORTHEAST : Path.DIR_SOUTHEAST;
} else {
return (y < this.y) ? DIR_NORTHWEST : DIR_SOUTHWEST;
return (y < this.y) ? Path.DIR_NORTHWEST : Path.DIR_SOUTHWEST;
}
}
/** The number of distinct directions the character may face. */
protected static final int NUM_DIRECTIONS = 8;
// Direction constants
protected static final int DIR_SOUTH = 0;
protected static final int DIR_SOUTHWEST = 1;
protected static final int DIR_WEST = 2;
protected static final int DIR_NORTHWEST = 3;
protected static final int DIR_NORTH = 4;
protected static final int DIR_NORTHEAST = 5;
protected static final int DIR_EAST = 6;
protected static final int DIR_SOUTHEAST = 7;
/** The number of frames of animation for each direction. */
protected static final int NUM_DIR_FRAMES = 8;
@@ -1,5 +1,5 @@
//
// $Id: Sprite.java,v 1.3 2001/07/31 01:38:28 shaper Exp $
// $Id: Sprite.java,v 1.4 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.sprite;
@@ -25,13 +25,14 @@ public class Sprite
/**
* Construct a Sprite object.
*
* @param spritemgr the sprite manager.
* @param x the sprite x-position in pixels.
* @param y the sprite y-position in pixels.
* @param tiles the tiles used to display the sprite.
*/
public Sprite (int x, int y, Tile[] tiles)
public Sprite (SpriteManager spritemgr, int x, int y, Tile[] tiles)
{
init(x, y, tiles);
init(spritemgr, x, y, tiles);
}
/**
@@ -39,29 +40,35 @@ public class Sprite
* sprite should be populated with a set of tiles used to display
* it via a subsequent call to <code>setTiles()</code>.
*
* @param spritemgr the sprite manager.
* @param x the sprite x-position in pixels.
* @param y the sprite y-position in pixels.
*/
public Sprite (int x, int y)
public Sprite (SpriteManager spritemgr, int x, int y)
{
init(x, y, null);
init(spritemgr, x, y, null);
}
/**
* Initialize the sprite object with the specified parameters.
* Initialize the sprite object with its variegated parameters.
*/
protected void init (int x, int y, Tile[] tiles)
protected void init (SpriteManager spritemgr, int x, int y, Tile[] tiles)
{
_spritemgr = spritemgr;
this.x = x;
this.y = y;
_curFrame = 0;
_animDelay = -1;
_numTicks = 0;
setTiles(_tiles);
_dest = new Point();
_state = STATE_NONE;
invalidate();
}
/**
@@ -72,6 +79,7 @@ public class Sprite
int xpos = x - (_curTile.width / 2);
int ypos = y - _curTile.height;
gfx.drawImage(_curTile.img, xpos, ypos, null);
// Log.info("Sprite painting image [x=" + xpos + ", y=" + ypos + "].");
}
/**
@@ -87,9 +95,22 @@ public class Sprite
*/
public boolean inside (int x, int y, int width, int height)
{
// treat the sprite as having a width and height of 1 pixel for now
// note that we consider the current tile for sprite
// width/height, and since the tile may change we're at the
// mercy of the tile creators to make sure all tiles for a
// given sprite are the same width.
// we might want to check this when tiles are set for the
// sprite, or require that the sprite width/height be
// specified separately when the sprite is created. or
// perhaps we'd like to be able to have sprites with variable
// width? i can't think why.
return (this.x >= x && this.x < (x + width) &&
this.y >= y && this.y < (y + height));
// return (this.x >= x && this.x + _curTile.width < (x + width) &&
// this.y >= y && this.y + _curTile.height < (y + height));
}
/**
@@ -113,6 +134,7 @@ public class Sprite
_tiles = tiles;
if (_tiles != null) {
_curTile = _tiles[_curFrame];
invalidate();
}
}
@@ -143,6 +165,25 @@ public class Sprite
_state = STATE_MOVING;
}
/**
* Invalidate the sprite's display rectangle for later repainting.
*/
public void invalidate ()
{
if (_curTile == null) return;
int xpos = x - (_curTile.width / 2);
int ypos = y - _curTile.height;
Rectangle dirty =
new Rectangle(xpos, ypos, _curTile.width, _curTile.height);
// Log.info("Sprite dirtying rect [x=" + dirty.x + ", y=" + dirty.y +
// ", width=" + dirty.width + ", height=" + dirty.height + "].");
_spritemgr.addDirtyRect(dirty);
}
/**
* This method is called periodically by the SpriteManager to give
* the sprite a chance to update its state.
@@ -154,6 +195,7 @@ public class Sprite
_numTicks = 0;
if (++_curFrame > _tiles.length - 1) _curFrame = 0;
_curTile = _tiles[_curFrame];
invalidate();
}
switch (_state) {
@@ -170,6 +212,9 @@ public class Sprite
x = _dest.x;
y = _dest.y;
// invalidate the sprite in its new location
invalidate();
// and note our stoppage
_animDelay = -1;
_state = STATE_NONE;
@@ -178,7 +223,7 @@ public class Sprite
}
}
// State constants.
/** State constants. */
protected static final int STATE_NONE = 0;
protected static final int STATE_MOVING = 1;
@@ -208,4 +253,7 @@ public class Sprite
/** The number of ticks since the last tile animation. */
protected int _numTicks;
/** The sprite manager. */
protected SpriteManager _spritemgr;
}
@@ -1,9 +1,10 @@
//
// $Id: SpriteManager.java,v 1.3 2001/07/31 01:38:28 shaper Exp $
// $Id: SpriteManager.java,v 1.4 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.sprite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import com.threerings.miso.Log;
@@ -19,6 +20,17 @@ public class SpriteManager
public SpriteManager ()
{
_sprites = new ArrayList();
_dirty = new ArrayList();
}
/**
* Add a rectangle to the dirty rectangle list.
*
* @param rect the rectangle to add.
*/
public void addDirtyRect (Rectangle rect)
{
_dirty.add(rect);
}
/**
@@ -31,6 +43,38 @@ public class SpriteManager
_sprites.add(sprite);
}
/**
* Return the list of dirty rects in screen pixel coordinates that
* have been created by any sprites since the last time the dirty
* rects were requested.
*
* @return the list of dirty rects.
*/
public ArrayList getDirtyRects ()
{
// create a copy of the dirty rectangles
ArrayList dirty = (ArrayList)_dirty.clone();
// clear out the list
_dirty.clear();
// return the full original list
return dirty;
}
/**
* Start a sprite moving along a particular path. The sprite will
* continue to be moved along the path until the final destination
* is reached, or until the sprite is brought to a halt by some
* has-yet-to-be-determined means.
*
* @param sprite the sprite to move.
* @param path the path to move the sprite along.
*/
public void moveSprite (Sprite sprite, Path path)
{
}
/**
* Render the sprites residing within the specified pixel bounds
* to the given graphics context.
@@ -58,9 +102,9 @@ public class SpriteManager
}
/**
* Call <code>Sprite.tick()</code> on all sprite objects to give
* them a chance to move themselves about, change their display
* image, and so forth.
* Call <code>tick()</code> on all sprite objects to give them a
* chance to move themselves about, change their display image,
* and so forth.
*/
public void tick ()
{
@@ -73,4 +117,7 @@ public class SpriteManager
/** The sprite objects we're managing. */
protected ArrayList _sprites;
/** The dirty rectangles created by sprites. */
protected ArrayList _dirty;
}
@@ -0,0 +1,93 @@
//
// $Id: LineSegmentPath.java,v 1.1 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.sprite;
import java.util.ArrayList;
import java.util.Enumeration;
/**
* 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.
*/
public class Path
{
/** The number of distinct directions. */
public static final int NUM_DIRECTIONS = 8;
/** Direction constants. */
public static final int DIR_SOUTH = 0;
public static final int DIR_SOUTHWEST = 1;
public static final int DIR_WEST = 2;
public static final int DIR_NORTHWEST = 3;
public static final int DIR_NORTH = 4;
public static final int DIR_NORTHEAST = 5;
public static final int DIR_EAST = 6;
public static final int DIR_SOUTHEAST = 7;
/**
* Construct a Path object.
*/
public Path ()
{
_nodes = new ArrayList();
}
/**
* Add a node to the path with the specified destination point and
* facing direction.
*
* @param x the x-position.
* @param y the y-position.
* @param dir the facing direction.
*/
public void addNode (int x, int y, int dir)
{
_nodes.add(new PathNode(x, y, dir));
}
/**
* Return an enumeration of the PathNode objects in this path.
*/
public Enumeration elements ()
{
return new Enumerator(_nodes);
}
/**
* Return the number of nodes in the path.
*/
public int size ()
{
return _nodes.size();
}
/**
* Internal class that provides enumeration functionality for the path.
*/
class Enumerator implements Enumeration
{
public Enumerator (ArrayList nodes)
{
_nodes = nodes;
_idx = 0;
}
public boolean hasMoreElements()
{
return (_idx < _nodes.size());
}
public Object nextElement ()
{
return _nodes.get(_idx++);
}
protected ArrayList _nodes;
protected int _idx;
}
/** The nodes that make up the path. */
protected ArrayList _nodes;
}
@@ -0,0 +1,31 @@
//
// $Id: PathNode.java,v 1.1 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.sprite;
import java.awt.Point;
/**
* The PathNode object is a single destination point in a Path.
*/
public class PathNode
{
/** The node coordinates in screen pixels. */
public Point loc;
/** The direction to face while heading toward the node. */
public int dir;
/**
* Construct a PathNode object.
*
* @param x the node x-position.
* @param y the node y-position.
* @param dir the facing direction.
*/
public PathNode (int x, int y, int dir)
{
loc = new Point(x, y);
this.dir = dir;
}
}
@@ -0,0 +1,163 @@
//
// $Id: PerformanceMonitor.java,v 1.1 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.util;
import java.util.HashMap;
import com.threerings.miso.Log;
/**
* The <code>PerformanceMonitor</code> class provides a simple
* mechanism for monitoring the number of times an action takes place
* within a certain time period.
*
* <p> The action being tracked should be registered with a suitable
* name via <code>register()</code>, and <code>tick()</code> should be
* called each time the action is performed.
*
* <p> Whenever <code>tick()</code> is called and the checkpoint time
* interval has elapsed since the last checkpoint (if any), the
* observer will be notified to that effect by a call to
* <code>PerformanceObserver.checkpoint()</code>.
*
* <p> Note that this is <em>not</em> intended to be used as an
* industrial-strength profiling or performance monitoring tool. The
* checkpoint time interval granularity is in milliseconds, not
* microseconds; and the observer's <code>checkpoint()</code> method
* will never be called until/unless a subsequent call to
* <code>tick()</code> is made after the requested number of
* milliseconds have passed since the last checkpoint.
*/
public class PerformanceMonitor
{
/**
* Register a new action with an observer, the action name, and
* the milliseconds to wait between checkpointing the action's
* performance.
*
* @param obs the action observer.
* @param name the action name.
* @param delta the milliseconds between checkpoints.
*/
public static void register (PerformanceObserver obs, String name,
long delta)
{
// get the observer's action hashtable
HashMap actions = (HashMap)_observers.get(obs);
if (actions == null) {
// create it if it didn't exist
_observers.put(obs, actions = new HashMap());
}
// add the action to the set we're tracking
actions.put(name, new PerformanceAction(obs, name, delta));
}
/**
* Un-register the named action associated with the given observer.
*
* @param obs the action observer.
* @param name the action name.
*/
public static void unregister (PerformanceObserver obs, String name)
{
// get the observer's action hashtable
HashMap actions = (HashMap)_observers.get(obs);
if (actions == null) {
Log.warning("Attempt to unregister by unknown observer " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// attempt to remove the specified action
PerformanceAction action = (PerformanceAction)actions.remove(name);
if (action == null) {
Log.warning("Attempt to unregister unknown action " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// if the observer has no actions left, remove the observer's action
// hash in its entirety
if (actions.size() == 0) {
_observers.remove(obs);
}
}
/**
* Tick the named action associated with the given observer.
*
* @param obs the action observer.
* @param name the action name.
*/
public static void tick (PerformanceObserver obs, String name)
{
// get the observer's action hashtable
HashMap actions = (HashMap)_observers.get(obs);
if (actions == null) {
Log.warning("Attempt to tick by unknown observer " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// get the specified action
PerformanceAction action = (PerformanceAction)actions.get(name);
if (action == null) {
Log.warning("Attempt to tick unknown value " +
"[observer=" + obs + ", name=" + name + "].");
return;
}
// tick the action
action.tick();
}
/** The observers monitoring some set of actions. */
protected static HashMap _observers = new HashMap();
}
class PerformanceAction
{
public PerformanceAction (PerformanceObserver obs, String name, long delta)
{
_obs = obs;
_name = name;
_delta = delta;
_lastDelta = System.currentTimeMillis();
}
public void tick ()
{
_numTicks++;
long now = System.currentTimeMillis();
if ((now - _lastDelta) >= _delta) {
// update the last checkpoint time
_lastDelta = now;
// notify our observer of the checkpoint
_obs.checkpoint(_name, _numTicks);
// reset the tick count
_numTicks = 0;
}
}
public String toString ()
{
StringBuffer buf = new StringBuffer();
buf.append("[obs=").append(_obs);
buf.append(", name=").append(_name);
buf.append(", delta=").append(_delta);
buf.append(", lastDelta=").append(_lastDelta);
buf.append(", numTicks=").append(_numTicks);
return buf.append("]").toString();
}
protected PerformanceObserver _obs;
protected String _name;
protected long _delta;
protected long _lastDelta;
protected int _numTicks;
}
@@ -0,0 +1,22 @@
//
// $Id: PerformanceObserver.java,v 1.1 2001/08/02 00:42:02 shaper Exp $
package com.threerings.miso.util;
/**
* The <code>PerformanceObserver</code> interface should be
* implemented by classes that wish to register actions to be
* monitored by the <code>PerformanceMonitor</code> class.
*/
public interface PerformanceObserver
{
/**
* This method is called by the <code>PerformanceMonitor</code>
* class whenever an action's requested time interval between
* checkpoints has expired.
*
* @param name the action name.
* @param ticks the ticks since the last checkpoint.
*/
public void checkpoint (String name, int ticks);
}