Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Java Game Programming For Dummies - W. Holder, D. Bell

.pdf
Скачиваний:
63
Добавлен:
24.05.2014
Размер:
5 Мб
Скачать
☆
Spri to
Sprite. java
S p r i t e

Chapter 8: 2-D Sprite Maze 175

r It defines its collision box. The collision box is a rectangular area that moves with the sprite and functions as the sprite's area of influence, determining where the sprite can collide with other sprites.

v It handles collisions with other sprites and with the edge of the play field.

Because game elements that are sprites may need to extend classes that aren't sprites, you use an interface to define the sprite methods. Using an interface allows any class that implements the interface to function as a sprite.

An interface is a definition of methods that a class implements in order to do the job that the interface defines.

The sprite interface you use for the game in this chapter, as well as for many other games, is quite simple. You declare the interface in the file

like this:

i mport java.awt.*;

public interface Sprite f

void

setSpriteEngine (SpriteEngine se);

boolean

updateSprite ();

Rectangle drawSprite (Graphics g);

Rectangle collisionBox ();

Rectangle eollideWith (Object obj);

1 ll end interface Sprite

The sprite engine uses the S p r i t e interface methods to manage sprites. Table 8-1 shows the responsibilities for each of the five methods the interface defines.

Table 8-1

Method

setSpriteEngine()

updateSprite()

The Sprite Interface Methods

What the Sprite Engine Expects It To Do

The sprite engine passes this method a reference to the engine when the sprite is added to the

engine, and calls it with nul 1 when the sprite is removed from the engine. The sprite uses the reference to call methods in the sprite engine.

This method is the sprite's heartbeat. The sprite engine calls this method periodically to tell the sprite to update its state. You move, animate, and initiate actions in update Spri te( ). updateSpri te( ) returns true if you change the sprite's collision box, f a 1 s e if you don't.

(continued)

Spri teObject
Spri teObject.
1 i d eW i t h ( )
Spri teBorder

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.

teOb j ect
Ob j ectDetect or

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);