Java Game Programming For Dummies - W. Holder, D. Bell
.pdf
176 Part II: Up to Speed
Table 8-1 (continued)
Method |
|
What the Sprite Engine Expects It To Do |
drawSprite() |
The sprite engine passes this method a graphics |
|
|
|
context in which to draw the sprite. It returns a |
|
|
Rectangl e representing the region drawn to the |
|
|
screen or n u 1 1 if it didn't draw anything. |
collisionBox() |
This method returns a Rectangl e containing the |
|
|
|
sprite's collision box. The sprite engine uses the |
|
|
collision box to determine whether the sprite |
|
|
collides with other sprites. coll i si onBox( ) |
|
|
returns nul 1 to indicate that this sprite doesn't |
|
|
collide with other sprites. |
collideWith() |
In the event of a collision, the sprite engine passes |
|
|
|
this method the object with which the sprite |
|
|
collided. After resolving the collision with |
|
|
whatever action is part of your game, |
|
|
coI 1 i desWi th( ) returns the possibly changed |
|
|
collision box for the sprite. |
The object parameter that the sprite engine passes to the sprite's
c o l l i d eW i t h ( ) method is usually another S p r i t e, but can also be one the constantsNORTH, SOUTH, EAST, or WEST-that
S p r i t e E n g i n e defines to represent collisions with the appropriate edge of
the play field. (Spri teBorder is an empty class that Spri teEngi ne uses to
define these object constants so that c o 1 can test for a border
collision using the test obj i nstanceof Spri teBorder.) col l i deWi th
resolves the collision, which may include changing the position of the sprtse c o l l i deWi t h ( ) returns the new collision box or n u 1 1 if the sprite engine manr not check for any more collisions with a given sprite during the current updat
Putting sprites in their place
Although some S p r i t e classes may need to extend a non-S p r i t e class. the main function of a Spri to class is usually just to be a sprite. In addition. th methods defined by the Spri to interface all have logical default implemen tations. These two features of sprites often enable you to encapsulate the common code shared between sprites and reduce the amount of code you have to write for each new sprite you create by giving these sprites a common superclass.
The S p r i t e O b j e c t class is exactly this superclass; it implements the Spri to interface. The code to keep track of a sprite's position and collisim box is pretty standard for all sprites, so you can implement it in
Here is the class:
Chapter 8: 2-D Sprite Maze 177
i mport java.awt.*;
public class SpriteObject implements Sprite I
protected double |
x, y; // center of sprite |
protected int |
width, height; |
protected SpriteEngine spriteEngine;
public SpriteObject (double x, double y, int w, int h) I
this.x = x; this.y = y; width |
= w; height = h; |
||
public double centerX () |
I |
return x; ) |
|
public double centerY () |
|
{ |
|
|
return y; l |
||
public int |
spriteWidth t) |
I |
return width; ) |
public int |
spriteHeight () |
1 |
return height; |
public void setSpriteEngine (SpriteEngine se) spriteEngine = se;
public boolean updateSprite () I return false; )
public Rectangle drawSprite (Graphics g) I return null; I public Rectangle collisionBox () I
return new Rectangle((int)(x - width / 2.0), (int)(y - height / 2.0), width, height);
public Rectangle collideWith (Object obj)
Ireturn collisionBox();
}// end class SpriteObject
Spri t e 0 b j e c t position sprites with an anchor point at the center of the sprite image. Using the center of the sprite image for the anchor point is a decision you need to make based on the kinds of actions your sprites need to be able to perform. You can position sprites from any of the nine locations shown in Figure 8-1. The width and height of a sprite may change as it is animated, but the anchor point continues to dictate how the sprite positions itself. You need to choose the anchor point based on how the sprite anchors itself to the background. We chose the center position as the default because without any other selection criteria, the center is the best choice.
However, some sprites may require anchor points at locations other than the center of the sprite image. For example, an explosion sprite animates from a small explosion image to a large explosion image. You want the explosion to grow out from the center, so you use a center anchor point. On the other hand, if your sprite represents a side view of a worm on the
78 Part II: Up to Speed
Figure 8-1:
The nine
ways to
position a
sprite's
anchor
point.
ground, you probably want to use a bottom anchor point to anchor the worm to the ground. Using a bottom anchor point, the sprite expands and shrinks from the center as its width changes, but keeps the bottom of the worm anchored to the ground as the height changes. Conversely, you probably want to use a top anchor point when you have a sprite that crawel across a ceiling.
Moving sprites around the play field
Notice that S p r i t e 0 b j e c t uses d o u b 1 e values for the x and y position the sprite. Given that you can only draw images at integer pixel locations. you may wonder why x and y are floating-point values. The answer lies in the fact that using floating-point values results in much smoother movement. Chapter 1 shows how you use floating-point coordinates and float point delta x and y values to smoothly move objects at any speed and in m direction.
You give your sprites motion by changing their position in the method
u p d a t e S p r i t e () . Here is an example of how you add simple vector motion to your sprite:
protected double deltaX, deltaY; // the vector deltas public boolean updateSprite () {
x += deltaX; y += deltaY; return true;
Notice that the movement code doesn't need to do any checking to see whether the object moves out of bounds or runs into something because I sprite engine handles all the collision detection. All updateSpri tell' r. _ to do is move the sprite.
Chapter 8:2- 0 Sprite Maze 179
Resolving collisions
The sprite engine takes care of detecting collisions, but the sprite itself is responsible for handling what happens as a result of the collision. When the sprite engine detects a collision, it calls the c o 1 1 i d e W i t h ( ) methods for each sprite involved in the collision. You implement col 1 i deWi th ( ) to
resolve the collision. (The "Implementing a sprite" section earlier in this chapter discusses how the sprite engine calls c o 1 1 i d e W i t h ( ).)
The Object Detector class extends Spri teObject. It detects when a specific object has collided with it and then notifies the sprite engine's
observers. (The observers are other objects, such as the S p r i t eMa z e game presented in the "Building on the B 1 o c k M a z e class" section later in this chapter, that receive messages from the sprites in the engine.) You can trigger an event when a sprite reaches a location in the play field by adding an sprite for that location to the sprite engine. The section "Sprite events and handling them" later in this chapter discusses how the notification process works. Here is the Ob j ectDetector class:
import java.awt.*;
class ObjectDetector extends SpriteObject
private Object target;
ObjectDetector(int x, i nt y, int w, int h, Object target){
super(x, y, w, h); this.target = target;
1
public Rectangle collideWith (Object obj) I
i f (obj =- target)
spriteEngine.notifyObservers(this);
return collisionBox();
4
11 end class ObjectDetector
You can use sprite collision detection to do proximity detection. Proximity detection is when you want a sprite to know when something is close to it before it collides. You use proximity detection as an early warning system to allow your sprite to change course, initiate defensive maneuvers, or launch an attack. You give a sprite proximity detection by adding a slave sprite with a col 1 i s i onBox ( ) that defines the detection perimeter around the master sprite. The slave sprite only needs to implement the col 1 i s i o n Box ( ) and c o l l i d e W i t h ( ) methods; you leave the other methods empty like the corresponding methods in the Spri class.
Part II: Up to Speed
Displaying sprites
You display a sprite by implementing the Sprite method d r a w S p r - : _ |
'!,.... |
|
The sprite engine passes d r a w S p r i t e ( ) the graphics context in which |
W) |
1 |
draw the sprite. As an example, the following R o u n d S p r i t e class draws sprite as a colored oval:
i mport java.awt.*;
class RoundSprite extends SpriteObject I
protected Color color;
RoundSprite (double x, double y, int w, int h, Color c) i
super(*, y, w, h); color = c;
public Rectangle drawSprite (Graphics g) {
g.setColor(color);
g.fillOval((int)(x - width / 2.0),
(int)(y - height / 2.0), width, height); return collisionBox():
end class RoundSprite
Notice in the call to f i 1 10 v a 1 ( ) that d r a wS p r i t e ( ) translates the spr s center anchor point to the upper-left corner of the sprite before drawing to match the upper-left coordinates that f i 1 10 v a 1 ( ) expects.
Drawing plain old colored geometric shapes has its place, but what you really need in order to give your game visual appeal are some colorful images zippin' around the screen. Here's an I ma g e S p r i t e class that you use to create sprites from loaded images:
i mport java.awt.*;
class ImageSprite extends SpriteObject I protected Image image:
ImageSprite (Image image, double x, double y) super(x, y, 0, 0);
setImage(image);
public void setImage (Image img) I i mage = img;
width = img.getWidth(null): height = img.getHeight(null);
