Drawing sprites and animation

Hey all, it’s me again asking for more object oriented help. ::slight_smile:

I have an abstract sprite class (used to be called “Entity” but I changed it to sprite for some reason) and I wasn’t sure how each type of sprite would draw itself, so I made a DrawStrategy interface and each Sprite holds one of these. So for instance, some sprites have 8 different images depending on what direction they are heading, some always use the same. Some have an animation cycle, some do not. I created different DrawStrategy classes for each type of drawing needed, and just add this to the Sprite when I create it.

The thing is, some strategies need more info than others. So I add methods like setDirection(Direction d) to a DirectionAnimationStrategy class (Direction is an Enum, BTW). But if a certain sprite child class (like Enemy) needs to call this, then I am forced to cast the DrawStrategy to DirectionAnimationStrategy. Here is what I am talking about:


package towerdefence.sprite;

import java.awt.geom.Point2D;
import towerdefence.GameWorld;
import towerdefence.graphics.DirectionAnimationStrategy;
import towerdefence.graphics.DrawStrategy;
import towerdefence.path.Path;


public class Enemy extends Sprite {
	
	private Path path;
	private double dist = 0;
	private double speed = 3;

	public Enemy(Point2D.Double l, double w, double h, GameWorld wo, DrawStrategy d) {
		super(l, w, h, wo, d);
		path = wo.getLevel().getPath();
	}

	public  void updateMe() {
		
		Point2D.Double lastLoc = location;
		
		setBounds();
		
		dist += speed;
		
		if(dist > path.length())
			dist = 0;
		
		location = path.getPointFromDistance(dist);
		
		double angle = Math.atan2(location.y - lastLoc.y, location.x - lastLoc.x);
		
		((DirectionAnimationStrategy)drawStrat).setDirection(Direction.directionFromAngle(angle));
		((DirectionAnimationStrategy)drawStrat).update();
	}

}

This seems a little messy to me. Is there a better way to do this?

By the way, Path is a class which tells the Enemy how it is to walk around the game world. I am making a tower defense game, just in case you missed my earlier thread, so enemies just walk along a predetermined path. I figured the best way to do this is to store the path as a spline.

EDIT:

For reference, here is my Sprite class:

package towerdefence.sprite;

import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import towerdefence.GameWorld;
import towerdefence.graphics.DrawStrategy;

public abstract class Sprite {

	protected Point2D.Double location;
	protected GameWorld world;
	protected DrawStrategy drawStrat;
	private Rectangle2D.Double bounds;
	private double width, height;
	
	private boolean drawingBounds;
	
	public Sprite(Point2D.Double l, double w, double h, GameWorld wo, DrawStrategy d) {
		location = l;
		world = wo;
		drawStrat = d;
		
		width = w;
		height = h;
		
		bounds = new Rectangle2D.Double();
		setBounds();
	}
	
	public void setDrawStrategy(DrawStrategy d) {
		drawStrat = d;
	}
	
	public void drawMe(Graphics2D g) {
		g.translate(location.x - width/2, location.y - height/2);
		drawStrat.draw(g);
		
		g.translate(-location.x + width/2, -location.y + height/2);
		
		if(drawingBounds)
			g.draw(bounds);
	}
	
	public abstract void updateMe();
	
	protected void setBounds() {
		bounds.setRect(location.x - width/2.0, location.y - height/2.0, width, height);
	}
	
	// Methods for collision detection:
	
	public Rectangle2D getBounds() {
		return bounds;
	}
	
	public boolean collidesWith(Rectangle2D r) {
		return bounds.intersects(r);
	}
	
	public boolean collidesWith(Sprite s) {	
		return collidesWith(s.getBounds());
	}

	public boolean isDrawingBounds() {
		return drawingBounds;
	}

	public void setDrawingBounds(boolean drawingBounds) {
		this.drawingBounds = drawingBounds;
	}
	
}

There is no way that the code in enemy.updateMe can work unless the DrawStrategy it is given in the constructor is a DirectionAnimationStrategy. So the constructor should take a DirectionAnimationStrategy, and not a plain DrawStrategy. You can still treat it as a DrawStrategy (without any casting) for other methods that only need a DrawStrategy and don’t otherwise need to be aware of the direction.

Oh so I should just edit the constructor in Enemy? I didn’t think of that… :clue:

Would you say the way I have set all this business up is a good way to do it?

I tend to think of a Sprite as the mechanism for drawing in the first place, so a drawing strategy like how to animate is simply a property of the sprite. If I had a class that selected a different sprite based on its facing or position or whatever state, I might call it a Model, similar to a 3D model with rigging that can put into different poses. This is just my particular design taste, and I don’t write action games (my experience is with MUDs) so take my design notes with a grain of salt…

I can’t right now, but if you want later on I can post some simple animation code I wrote?

Sure, I would appreciate it if you have the time. ;D


/**
 * This class is the class that is used for animtions.
 * @author Pearce Keesling aka h3ckboy
 *
 */
import java.awt.*;

import java.awt.image.BufferedImage;
import java.awt.Container;
public class Animation implements Cloneable {
	//the frames for the animation
	public BufferedImage[] images;
	//the time between each frame
	int time;
	//time that has passed since last frame
	int elapsedTime = 0;
	int currentFrame;
	public Animation(BufferedImage[] images,int time)
	{
		this.images = images;
		this.time = time;
		currentFrame = 0;
	}
	//advance animation each frame
	public void advance(int time)
	{
		elapsedTime+=time;
		if(elapsedTime>=this.time)
		{
			elapsedTime -=this.time;
			currentFrame++;
			if(currentFrame>=images.length)
			{
				currentFrame = 0;
			}
		}
	}
	//render the animation's current frame
	public void render(Graphics g,int x, int y,Container container)
	{
		g.drawImage(images[currentFrame],x,y,container);
	}
	public void render(Graphics g,int x, int y,int direction,Container container)
	{
		BufferedImage[] images = setDirection(direction,container);
		g.drawImage(images[currentFrame],x,y,container);
	}
	public BufferedImage getCurrentFrame()
	{
		return images[currentFrame];
	}
	public int getCurrentFrameInt()
	{
		return currentFrame;
	}
}

This is the animation class that I use, I tried to cut out some extra stuff I had in that wasn’t exactly relevant to animation. feel free to use any or all of it, it was designed to be stand-alone from my programs.

If you have any questions about implementations just ask.