
com.threerings.media.sprite.Sprite Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nenya Show documentation
Show all versions of nenya Show documentation
Facilities for making networked multiplayer games.
The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sprite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import com.samskivert.util.ObserverList;
import com.threerings.util.DirectionCodes;
import com.threerings.media.AbstractMedia;
import com.threerings.media.util.Path;
import com.threerings.media.util.Pathable;
/**
* The sprite class represents a single movable object in an animated view. A sprite has a
* position and orientation within the view, and can be moved along a path.
*/
public abstract class Sprite extends AbstractMedia
implements DirectionCodes, Pathable
{
/**
* Constructs a sprite with an initially invalid location. Because sprite derived classes
* generally want to get in on the business when a sprite's location is set, it is not safe to
* do so in the constructor because their derived methods will be called before their
* constructor has been called. Thus a sprite should be fully constructed and then its
* location should be set.
*/
public Sprite ()
{
this(0, 0);
}
/**
* Constructs a sprite with the supplied dimensions. Because sprite derived classes generally
* want to get in on the business when a sprite's location is set, it is not safe to do so in
* the constructor because their derived methods will be called before their constructor has
* been called. Thus a sprite should be fully constructed and then its location should
* be set.
*/
public Sprite (int width, int height)
{
super(new Rectangle(0, 0, width, height));
}
/**
* Returns the sprite's x position in screen coordinates. This is the x coordinate of the
* sprite's origin, not the upper left of its bounds.
*/
public int getX ()
{
return _ox;
}
/**
* Returns the sprite's y position in screen coordinates. This is the y coordinate of the
* sprite's origin, not the upper left of its bounds.
*/
public int getY ()
{
return _oy;
}
/**
* Returns the offset to the sprite's origin from the upper-left of the sprite's image.
*/
public int getXOffset ()
{
return _oxoff;
}
/**
* Returns the offset to the sprite's origin from the upper-left of the sprite's image.
*/
public int getYOffset ()
{
return _oyoff;
}
/**
* Returns the sprite's width in pixels.
*/
public int getWidth ()
{
return _bounds.width;
}
/**
* Returns the sprite's height in pixels.
*/
public int getHeight ()
{
return _bounds.height;
}
/**
* Sprites have an orientation in one of the eight cardinal directions:
* {@link DirectionCodes#NORTH}, {@link DirectionCodes#NORTHEAST}, etc. 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.
*
* @see DirectionCodes
*/
public void setOrientation (int orient)
{
_orient = orient;
}
/**
* Returns the sprite's orientation as one of the eight cardinal directions:
* {@link DirectionCodes#NORTH}, {@link DirectionCodes#NORTHEAST}, etc.
*
* @see DirectionCodes
*/
public int getOrientation ()
{
return _orient;
}
@Override
public void setLocation (int x, int y)
{
if (x == _ox && y == _oy) {
return; // no-op
}
// make a note of our current bounds
Rectangle obounds = new Rectangle(_bounds);
// move ourselves
_ox = x;
_oy = y;
// we need to update our draw position which is based on the size of our current bounds
updateRenderOrigin();
// this method will invalidate our old and new bounds efficiently
invalidateAfterChange(obounds);
}
@Override
public void paint (Graphics2D gfx)
{
gfx.drawRect(_bounds.x, _bounds.y, _bounds.width-1, _bounds.height-1);
}
/**
* Paint the sprite's path, if any, to the specified graphics context.
*
* @param gfx the graphics context.
*/
public void paintPath (Graphics2D gfx)
{
if (_path != null) {
_path.paint(gfx);
}
}
/**
* Returns true if the sprite's bounds contain the specified point, false if not.
*/
public boolean contains (int x, int y)
{
return _bounds.contains(x, y);
}
/**
* Returns true if the sprite's bounds contain the specified point, false if not.
*/
public boolean hitTest (int x, int y)
{
return _bounds.contains(x, y);
}
/**
* Returns whether the sprite is inside the given shape in pixel coordinates.
*/
public boolean inside (Shape shape)
{
return shape.contains(_ox, _oy);
}
/**
* Returns whether the sprite's drawn rectangle intersects the given shape in pixel coordinates.
*/
public boolean intersects (Shape shape)
{
return shape.intersects(_bounds);
}
/**
* Returns true if this sprite is currently following a path, false if it is not.
*/
public boolean isMoving ()
{
return (_path != null);
}
/**
* Set the sprite's active path and start moving it along its merry way. If the sprite is
* already moving along a previous path the old path will be lost and the new path will begin
* to be traversed.
*
* @param path the path to follow.
*/
public void move (Path path)
{
// if there's a previous path, let it know that it's going away
cancelMove();
// save off this path
_path = path;
// we'll initialize it on our next tick thanks to a zero path stamp
_pathStamp = 0;
}
/**
* Cancels any path that the sprite may currently be moving along.
*/
public void cancelMove ()
{
if (_path != null) {
Path oldpath = _path;
_path = null;
oldpath.wasRemoved(this);
if (_observers != null) {
_observers.apply(new CancelledOp(this, oldpath));
}
}
}
/**
* Returns the path being followed by this sprite or null if the sprite is not following a path.
*/
public Path getPath ()
{
return _path;
}
/**
* Called by the active path when it begins.
*/
public void pathBeginning ()
{
// nothing for now
}
/**
* Called by the active path when it has completed.
*/
public void pathCompleted (long timestamp)
{
Path oldpath = _path;
_path = null;
oldpath.wasRemoved(this);
if (_observers != null) {
_observers.apply(new CompletedOp(this, oldpath, timestamp));
}
}
@Override
public void tick (long tickStamp)
{
tickPath(tickStamp);
}
/**
* Ticks any path assigned to this sprite.
*
* @return true if the path relocated the sprite as a result of this tick, false if it
* remained in the same position.
*/
protected boolean tickPath (long tickStamp)
{
if (_path == null) {
return false;
}
// initialize the path if we haven't yet
if (_pathStamp == 0) {
_path.init(this, _pathStamp = tickStamp);
}
// it's possible that as a result of init() the path completed and removed itself with a
// call to pathCompleted(), so we have to be careful here
return (_path == null) ? true : _path.tick(this, tickStamp);
}
@Override
public void fastForward (long timeDelta)
{
// fast forward any path we're following
if (_path != null) {
_path.fastForward(timeDelta);
}
}
/**
* Update the coordinates at which the sprite image is drawn to reflect the sprite's current
* position.
*/
protected void updateRenderOrigin ()
{
// our bounds origin may differ from the sprite's origin
_bounds.x = _ox - _oxoff;
_bounds.y = _oy - _oyoff;
}
/**
* Add a sprite observer to observe this sprite's events.
*
* @param obs the sprite observer.
*/
public void addSpriteObserver (Object obs)
{
addObserver(obs);
}
/**
* Remove a sprite observer.
*/
public void removeSpriteObserver (Object obs)
{
removeObserver(obs);
}
@Override
public void viewLocationDidChange (int dx, int dy)
{
if (_renderOrder >= HUD_LAYER) {
setLocation(_ox + dx, _oy + dy);
}
}
@Override
protected void shutdown ()
{
super.shutdown();
cancelMove(); // cancel any active path
}
@Override
protected void toString (StringBuilder buf)
{
super.toString(buf);
buf.append(", ox=").append(_ox);
buf.append(", oy=").append(_oy);
buf.append(", oxoff=").append(_oxoff);
buf.append(", oyoff=").append(_oyoff);
}
/** Used to dispatch {@link PathObserver#pathCancelled}. */
protected static class CancelledOp implements ObserverList.ObserverOp
© 2015 - 2025 Weber Informatics LLC | Privacy Policy