|
|
What's that elfish?
We start by defining a very general, abstract class for sprites.frilled.cs.indiana.edu%pwd /nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three frilled.cs.indiana.edu%webster sprite sprite \'spr[0xF5]^-t\ n [ME sprit, fr. MF esprit, fr. L spiritus spirit -- more at SPIRIT] (14c) 1a archaic: SOUL 1b: a disembodied spirit: GHOST 2a: ELF, FAIRY 2b: an elfish person frilled.cs.indiana.edu%
frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three
frilled.cs.indiana.edu%ls -ld Sprite.java
-rw-r--r-- 1 dgerman 758 Nov 4 23:05 Sprite.java
frilled.cs.indiana.edu%cat Sprite.java
import java.awt.*;
abstract class Sprite {
protected boolean visible; // is sprite visible
protected boolean active; // is sprite updatable
// abstract methods:
abstract void paint (Graphics g);
abstract void update();
// accessor methods:
public boolean isVisible() {
return visible;
}
public void setVisible(boolean b) {
visible = b;
}
public boolean isActive() {
return active;
}
public void setActive(boolean b) {
active = b;
}
// suspend the sprite
public void suspend() {
setVisible(false);
setActive(false);
}
// restore the sprite
public void restore() {
setVisible(true);
setActive(true);
}
}
frilled.cs.indiana.edu%
We then describe a more specialized (two-dimensional) type of sprite.
frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three
frilled.cs.indiana.edu%ls -ld Sprite2D.java
-rw-r--r-- 1 dgerman 393 Nov 4 23:20 Sprite2D.java
frilled.cs.indiana.edu%cat Sprite2D.java
import java.awt.*;
abstract class Sprite2D extends Sprite {
protected int locx;
protected int locy;
Color color;
boolean fill;
public boolean getFill() {
return fill;
}
public void setFill(boolean b) {
fill = b;
}
public void setColor(Color c) {
color = c;
}
public Color getColor() {
return color;
}
}
frilled.cs.indiana.edu%
And then we specialize this description even further:
frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three
frilled.cs.indiana.edu%ls -ld RectSprite.java
-rw-r--r-- 1 dgerman 728 Nov 4 23:45 RectSprite.java
frilled.cs.indiana.edu%cat RectSprite.java
import java.awt.*;
class RectSprite extends Sprite2D {
protected int width, height; // dimensions of rectangle
public RectSprite(int x,int y,int w,int h,Color c) {
locx = x;
locy = y;
width = w;
height = h;
color = c;
fill = false; // default: don't fill
restore(); // restore the sprite
}
// provide implementation of abstract methods:
public void update() {
// does nothing
}
// check if sprite's visible before painting
public void paint(Graphics g) {
if (visible) {
g.setColor(color);
if (fill) {
g.fillRect(locx,locy,width,height);
} else {
g.drawRect(locx,locy,width,height);
}
}
}
}
frilled.cs.indiana.edu%
Finally, we describe what a bouncing rectangle is to us.
frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three
frilled.cs.indiana.edu%ls -ld Bouncing*
-rw-r--r-- 1 dgerman 1100 Nov 4 23:45 BouncingRect.java
frilled.cs.indiana.edu%cat Bouncing.java
import java.awt.*;
class BouncingRect extends RectSprite implements Moveable {
// the coords at which
// the rectangle bounces
protected int max_width;
protected int max_height;
// sprite velocity. used to implement Moveable interface
protected int vx;
protected int vy;
public BouncingRect(int x,int y,int w,int h,Color c,
int max_w,int max_h) {
super(x,y,w,h,c);
max_width = max_w;
max_height = max_h;
}
public void setPosition(int x,int y) {
locx = x;
locy = y;
}
public void setVelocity(int x,int y) {
vx = x;
vy = y;
}
// update position according to velocity
public void updatePosition() {
locx += vx;
locy += vy;
}
// move and bounce rectangle if it hits borders
public void update() {
// flip x velocity if it hits left or right bound
if ((locx + width > max_width) ||
locx < 0) {
vx = -vx;
}
// flip y velocity if it hits top or bottom bound
if ((locy + height > max_height) ||
locy < 0) {
vy = -vy;
}
updatePosition();
}
}
frilled.cs.indiana.edu%
A description of the moveable interface is needed.
frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three
frilled.cs.indiana.edu%ls -ld Move*
-rw-r--r-- 1 dgerman 170 Nov 4 23:38 Moveable.java
frilled.cs.indiana.edu%cat Move*.java
interface Moveable {
public abstract void setPosition(int c, int d);
public abstract void setVelocity(int x, int y);
public abstract void updatePosition();
}
frilled.cs.indiana.edu%
So now we can write an applet.
frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three
frilled.cs.indiana.edu%ls -ld Bounce.java Seven.html
-rw-r--r-- 1 dgerman 2357 Nov 4 23:48 Bounce.java
-rw-r--r-- 1 dgerman 247 Nov 4 23:49 Seven.html
frilled.cs.indiana.edu%cat Bounce.java
import java.applet.*;
import java.awt.*;
public class Bounce extends Applet implements Runnable {
Thread animation;
Graphics offscreen;
Image image;
static final int NUM_SPRITES = 3;
static final int REFRESH_RATE = 80; // in ms
Sprite sprites[]; // sprite array
int width, height; // applet dimensions
public void init() {
System.out.println(">> init <<");
setBackground(Color.black); // applet background
width = bounds().width; // set applet dimensions
height = bounds().height;
initSprites();
image = createImage(width,height); // make offscreen buffer
offscreen = image.getGraphics();
}
public void initSprites() {
sprites = new Sprite[NUM_SPRITES]; // init sprite array
// define sprite for border
sprites[0] = new RectSprite(0,0,width-1,height-1,Color.green);
sprites[1] = new BouncingRect(0,0,30,30,Color.yellow,
width-1,height-1);
sprites[2] = new BouncingRect(17,17,13,13,Color.red,
width-1,height-1);
((Moveable)sprites[1]).setVelocity(4,3);
((Moveable)sprites[2]).setVelocity(1,2);
((Sprite2D)sprites[2]).setFill(true); // fill this sprite
}
public void start() {
System.out.println("Starting... ");
animation = new Thread(this);
if (animation != null) {
animation.start();
}
}
// CALL EACH SPRITE'S update() METHOD
// DYNAMIC METHOD BINDING OCCURS HERE!
public void updateSprites() {
for (int i=0; i<sprites.length; i++) {
sprites[i].update(); // call each sprite's update() method
}
}
// override update so it doesn't erase screen
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
offscreen.setColor(Color.black);
offscreen.fillRect(0,0,width,height); // clear buffer
for (int i=0; i<sprites.length; i++) {
sprites[i].paint(offscreen); // paint each rectangle
}
g.drawImage(image,0,0,this);
}
public void run() {
while (true) {
repaint();
updateSprites();
try {
Thread.sleep (REFRESH_RATE);
} catch (Exception exc) { };
}
}
public void stop() {
System.out.println("Stopping... ");
if (animation != null) {
animation.stop();
animation = null;
}
}
}
frilled.cs.indiana.edu%cat Seven.html
<html>
<head><title>Bouncing... </title></head>
<body bgcolor=white>
Sprites bouncing... (see the notes) <p>
<applet code="Bounce.class" width=300 height=300>
</applet>
Dedicated to Michael Jordan. <p>
</body>
</html>
frilled.cs.indiana.edu%
And
here's how the applet runs. This is a diagram of the program so far.
We now change it, as follows. You will soon see why.
Classes already there have been downplayed with a pink shade of lightgrey.
So we can better focus on the new addition, which we present below.
We therefore need to present only three new classes and an HTML file.
An image will be used:
A sound file will be used as well.
And
here's the applet.
Let's now post the code. There are three new classes in all:
frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/three
frilled.cs.indiana.edu%ls -ld *.java
-rw-r--r-- 1 dgerman 696 Nov 5 12:33 BitmapSprite.java
-rw-r--r-- 1 dgerman 2357 Nov 4 23:48 Bounce.java
-rw-r--r-- 1 dgerman 1116 Nov 5 13:08 BouncingBitmap.java
-rw-r--r-- 1 dgerman 1100 Nov 4 23:45 BouncingRect.java
-rw-r--r-- 1 dgerman 2914 Nov 5 17:12 BouncingSushi.java
-rw-r--r-- 1 dgerman 170 Nov 4 23:38 Moveable.java
-rw-r--r-- 1 dgerman 728 Nov 4 23:45 RectSprite.java
-rw-r--r-- 1 dgerman 758 Nov 4 23:05 Sprite.java
-rw-r--r-- 1 dgerman 393 Nov 4 23:20 Sprite2D.java
frilled.cs.indiana.edu%cat BouncingSushi.java
import java.applet.*;
import java.awt.*;
public class BouncingSushi extends Applet implements Runnable {
Thread animation;
Graphics offscreen;
Image image;
AudioClip a;
static final int NUM_SPRITES = 6;
static final int REFRESH_RATE = 80; // in ms
Sprite sprites[]; // sprite array
int width, height; // applet dimensions
public void init() {
System.out.println("Initializing... ");
width = bounds().width; // set applet dimensions
height = bounds().height;
initSprites();
image = createImage(width,height); // make offscreen buffer
offscreen = image.getGraphics();
}
public void initSprites() {
sprites = new Sprite[NUM_SPRITES]; // init sprite array
// define sprite for border
sprites[5] = new RectSprite(0,0,width-1,height-1,Color.green);
sprites[1] = new BouncingRect(0,0,30,30,Color.yellow,
width-1,height-1);
sprites[2] = new BouncingRect(17,17,13,13,Color.red,
width-1,height-1);
// border of the smaller box
sprites[3] = new RectSprite(0,0,114,114,Color.green);
// this rect bounces in a smaller box!
sprites[4] = new BouncingRect(13,13,17,17,Color.green,
114,114);
sprites[0] = new BouncingBitmap(37,37,
getImage(getCodeBase(),
"MM.jpg"),
this,
width-1,height-1);
((Moveable)sprites[1]).setVelocity(7,5);
((Moveable)sprites[2]).setVelocity(1,2);
((Moveable)sprites[4]).setVelocity(3,4);
((Sprite2D)sprites[4]).setFill(true); // fill this sprite
((Moveable)sprites[0]).setVelocity(4,3);
((BitmapSprite)sprites[0]).setSize(134,168);
}
public void start() {
System.out.println("Starting... ");
animation = new Thread(this);
if (animation != null) {
animation.start();
}
a = getAudioClip(getCodeBase(), "Sound.au");
a.loop() ; // loop the sound
}
// CALL EACH SPRITE'S update() METHOD
// DYNAMIC METHOD BINDING OCCURS HERE!
public void updateSprites() {
for (int i=0; i<sprites.length; i++) {
sprites[i].update();
// call each sprite's update() method
}
}
// override update so it doesn't erase screen
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
offscreen.setColor(Color.black);
offscreen.fillRect(0,0,width,height); // clear buffer
for (int i=0; i<sprites.length; i++) {
sprites[i].paint(offscreen); // paint each rectangle
}
g.drawImage(image,0,0,this);
}
public void run() {
while (true) {
repaint();
updateSprites();
try {
Thread.sleep (REFRESH_RATE);
} catch (Exception exc) { };
}
}
public void stop() {
System.out.println("Stopping... ");
a.stop(); // stop the sound
if (animation != null) {
animation.stop();
animation = null;
}
}
}
frilled.cs.indiana.edu%cat BitmapSprite.java
import java.applet.*;
import java.awt.*;
class BitmapSprite extends Sprite {
protected int locx;
protected int locy;
// image dimensions
protected int width,height;
protected Image image; // the bitmap
protected Applet applet; // the parent applet
public BitmapSprite(int x,int y,Image i,Applet a) {
locx = x;
locy = y;
image = i;
applet = a;
restore();
}
public void setSize(int w,int h) {
width = w;
height = h;
}
public void update() {
// do nothing
}
public void paint(Graphics g) {
if (visible) {
g.drawImage(image,locx,locy,applet);
}
}
}
frilled.cs.indiana.edu%cat BouncingBitmap.java
import java.applet.*;
import java.awt.*;
class BouncingBitmap extends BitmapSprite implements Moveable {
// the coords at which the bitmap bounces
protected int max_width;
protected int max_height;
// sprite velocity. used to implement Moveable interface
protected int vx;
protected int vy;
public BouncingBitmap(int x,int y,Image i,Applet a,
int max_w,int max_h) {
super(x,y,i,a);
max_width = max_w;
max_height = max_h;
}
public void setPosition(int x,int y) {
locx = x;
locy = y;
}
public void setVelocity(int x,int y) {
vx = x;
vy = y;
}
// update position according to velocity
public void updatePosition() {
locx += vx;
locy += vy;
}
// move and bounce rectangle if it hits borders
public void update() {
// flip x velocity if it hits left or right bound
if ((locx + width > max_width) ||
locx < 0) {
vx = -vx;
}
// flip y velocity if it hits top or bottom bound
if ((locy + height > max_height) ||
locy < 0) {
vy = -vy;
}
updatePosition();
}
}
frilled.cs.indiana.edu%javac BouncingSushi.java
Note: BouncingSushi.java uses or overrides a deprecated API.
Note: Recompile with -deprecation for details.
frilled.cs.indiana.edu%ls -ld *.class
-rw-r--r-- 1 dgerman 857 Nov 5 17:09 BitmapSprite.class
-rw-r--r-- 1 dgerman 2633 Nov 5 17:09 Bounce.class
-rw-r--r-- 1 dgerman 901 Nov 5 17:12 BouncingBitmap.class
-rw-r--r-- 1 dgerman 875 Nov 5 17:12 BouncingRect.class
-rw-r--r-- 1 dgerman 3265 Nov 5 17:31 BouncingSushi.class
-rw-r--r-- 1 dgerman 180 Nov 5 17:09 Moveable.class
-rw-r--r-- 1 dgerman 804 Nov 5 17:09 RectSprite.class
-rw-r--r-- 1 dgerman 700 Nov 5 17:09 Sprite.class
-rw-r--r-- 1 dgerman 568 Nov 5 17:09 Sprite2D.class
frilled.cs.indiana.edu%ls -ld *.jpg *.au
-rw-r--r-- 1 dgerman 4550 Nov 5 14:24 MM.jpg
-rw-r--r-- 1 dgerman 28106 Nov 5 12:21 Sound.au
frilled.cs.indiana.edu%
The HTML file for this applet is here:
<html>
<head><title>A Weird Applet...</title></head>
<body bgcolor=white>
This <em>may</em> look a bit weird, but that's OK,
because it <em>is</em> weird...
<p>
<applet code="BouncingSushi.class" width=300 height=300>
</applet>
<p>
Dedicated to Andy Kauffman.
</body>
</html>
In this chapter you've learned about abstract classes and interfaces and how they permit
the clean, modular design of applets. In particular you've created Sprite classes
that you'll use in your first Java game (which is coming up really soon!). And you now know how
to use bitmaps, which are important in creating customized looks for your graphics applets. You
also saw how you can include sounds in your applets. Next you will see how to take control of
your sprites.