![]() |
![]() Second Summer 2002 |
Run this a few times (here's once):class One { public static void main(String[] args) { Two larry = new Two("Larry Bird", 7000); Two michael = new Two("Michael Jordan", 3000); // Thread one = new Thread(larry); // Thread two = new Thread(michael); System.out.println("Program started on " + new java.util.Date()); larry.run(); michael.run(); // one.start(); // two.start(); } } class Two /*implements Runnable*/ { String name; long delay; Two (String name, long delay) { this.name = name; this.delay = delay; } public void run() { try { Thread.sleep(delay); } catch (Exception e) { } System.out.println(name + " at " + new java.util.Date()); } }
Then we make a few changes (in blue).frilled.cs.indiana.edu%javac One.java frilled.cs.indiana.edu%java One Program started on Tue Aug 07 19:02:27 EDT 2001 Larry Bird at Tue Aug 07 19:02:34 EDT 2001 Michael Jordan at Tue Aug 07 19:02:37 EDT 2001 frilled.cs.indiana.edu%
If you run it, here's what it looks like:class One { public static void main(String[] args) { Two larry = new Two("Larry Bird", 7000); Two michael = new Two("Michael Jordan", 3000); Thread one = new Thread(larry); Thread two = new Thread(michael); System.out.println("Program started on " + new java.util.Date()); /* larry.run(); */ /* michael.run(); */ one.start(); two.start(); } } class Two implements Runnable { String name; long delay; Two (String name, long delay) { this.name = name; this.delay = delay; } public void run() { try { Thread.sleep(delay); } catch (Exception e) { } System.out.println(name + " at " + new java.util.Date()); } }
Now time runs simultaneously for both.frilled.cs.indiana.edu%java One Program started on Tue Aug 07 19:08:45 EDT 2001 Michael Jordan at Tue Aug 07 19:08:49 EDT 2001 Larry Bird at Tue Aug 07 19:08:53 EDT 2001 frilled.cs.indiana.edu%
So we make one last change.
Here's how it runs.class One { public static void main(String[] args) { Two larry = new Two("Larry Bird", 7000); Two michael = new Two("Michael Jordan", 3000); Thread one = new Thread(larry); Thread two = new Thread(michael); System.out.println("Program started on " + new java.util.Date()); one.start(); two.start(); } } class Two implements Runnable { String name; long delay; Two (String name, long delay) { this.name = name; this.delay = delay; } public void run() { while (true) { try { Thread.sleep(delay); } catch (Exception e) { } System.out.println(name + " at " + new java.util.Date()); } } }
So that's another way you can implement threads.frilled.cs.indiana.edu%java One Program started on Tue Aug 07 19:13:58 EDT 2001 Michael Jordan at Tue Aug 07 19:14:01 EDT 2001 Michael Jordan at Tue Aug 07 19:14:04 EDT 2001 Larry Bird at Tue Aug 07 19:14:05 EDT 2001 Michael Jordan at Tue Aug 07 19:14:07 EDT 2001 Michael Jordan at Tue Aug 07 19:14:10 EDT 2001 Larry Bird at Tue Aug 07 19:14:12 EDT 2001 Michael Jordan at Tue Aug 07 19:14:13 EDT 2001 Michael Jordan at Tue Aug 07 19:14:16 EDT 2001 Larry Bird at Tue Aug 07 19:14:19 EDT 2001 Michael Jordan at Tue Aug 07 19:14:19 EDT 2001 Michael Jordan at Tue Aug 07 19:14:22 EDT 2001 Michael Jordan at Tue Aug 07 19:14:25 EDT 2001 Larry Bird at Tue Aug 07 19:14:26 EDT 2001 Michael Jordan at Tue Aug 07 19:14:28 EDT 2001 Michael Jordan at Tue Aug 07 19:14:31 EDT 2001 Larry Bird at Tue Aug 07 19:14:33 EDT 2001 Michael Jordan at Tue Aug 07 19:14:34 EDT 2001 Michael Jordan at Tue Aug 07 19:14:37 EDT 2001 Larry Bird at Tue Aug 07 19:14:40 EDT 2001 Michael Jordan at Tue Aug 07 19:14:40 EDT 2001 Michael Jordan at Tue Aug 07 19:14:43 EDT 2001 Michael Jordan at Tue Aug 07 19:14:46 EDT 2001 Larry Bird at Tue Aug 07 19:14:47 EDT 2001 Michael Jordan at Tue Aug 07 19:14:49 EDT 2001 Michael Jordan at Tue Aug 07 19:14:52 EDT 2001 Larry Bird at Tue Aug 07 19:14:54 EDT 2001 Michael Jordan at Tue Aug 07 19:14:55 EDT 2001 Michael Jordan at Tue Aug 07 19:14:58 EDT 2001 frilled.cs.indiana.edu%
This particular way is quite convenient because
run()
method of a related thread.
What is a sprite?
A sprite is an elf.frilled.cs.indiana.edu%webster sprite sprite \'sprai-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%
Sprites arefrilled.cs.indiana.edu%webster elf elf \'elf\ n, pl elves \'elvz\ [ME, fr. OE aelf; akin to ON alfr elf & prob. to L albus white -- more at ALB] (bef. 12c) 1: a small often mischievous fairy 2a: a small lively creature; esp: a mischievous child 2b: a usu. lively mischievous or malicious person -- elf-ish \'el-fish\ adj -- elf-ish-ly adv frilled.cs.indiana.edu%
These elements could be
Here are ten bitmaps that have been designed with a clear goal in mind.
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T1.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T2.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T3.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T4.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T5.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T6.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T7.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T8.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T9.gif
http://www.cs.indiana.edu/classes/a202-dger/lectures/last/T10.gif
Let's describe a Sprite
. Here's a very general description.
Now the questions are:abstract class Sprite { protected boolean visible; public boolean isVisible() { return this.visible; } public void setVisible(boolean visibility) { this.visible = visibility; } protected boolean active; boolean isActive() { return this.active; } void setActive(boolean howActive) { this.active = howActive; } abstract void paint(Graphics g); abstract void update(); public void suspend() { this.setVisible(false); this.setActive(false); } public void restore() { this.setVisible(true); this.setActive(true); } }
abstract
class?
protected
?
abstract
method?
abstract
class is an unfinished class. It can't be instantiated, and can leave methods unimplemented.
For example our old inheritance example could be reworked as follows.
If you want to store circles and rectangles in an array you need to be able to look at them uniformly, so you need to use a class that abstracts the notion of shape (or, if you want, geometrical figure).class Example { public static void main(String[] args) { Shape[] shapes = new Shape[2]; shapes[0] = new Circle(50, 50, 1); shapes[1] = new Rectangle(100, 100, 2, 3); for (int i = 0; i < shapes.length; i++) { System.out.println(shapes[i].area()); } } } abstract class Shape { int x, y; Shape(int x, int y) { this.x = x; this.y = y; } abstract double area(); } class Circle extends Shape { int radius; Circle(int x, int y, int radius) { super(x, y); this.radius = radius; } double area() { return Math.PI * this.radius * this.radius; } } class Rectangle extends Shape { int width, height; Rectangle(int x, int y, int width, int height) { super(x, y); this.width = width; this.height = height; } double area() { return width * height; } }
Then if you want to be able to simply compute the areas by invoking a method with that
name (area
) without any casting you need to have a method with that name in
class Shape
.
Otherwise you'd need to cast or you would get an error.
Note that since you're overriding the area()
method in the subclasses
(Circle
and Rectangle
) the method defined in the class of
the object (as opposed to the one defined in the class of the reference) will be
executed when invoked. So areas are computed properly.
But what should the area()
method in class Shape
look like?
It should look like "not enough information to compute" and that's what it looks like in our example.
(If don't feel comfortable with the example, please refer to
Lecture Notes
Twenty-Eight).
Now let's become more concrete in our definitions.
Now let's define an interface.class BitmapSprite extends Sprite { protected int locx; protected int locy; protected Image image; protected Applet applet; public BitmapSprite(int x, int y, Image i, Applet a) { locx = x; locy = y; image = i; applet = a; this.restore(); } protected int width, height; public void setSize(int w, int h) { width = w; height = h; } public void update() { /* do nothing */ } public void paint(Graphics g) { if (this.visible) { if (image != null) g.drawImage(image, locx, locy, applet); } } }
And let's put all these definitions to work in our new gizmo.interface Moveable { public abstract void setPosition(int x, int y); public abstract void setVelocity(int x, int y); public abstract void updatePosition(); }
Now let's put this to use.class BitmapLoop extends BitmapSprite implements Moveable { protected Image images[]; protected int currentImage; protected boolean foreground; public BitmapLoop (int x, int y, Image background, Image foreground[], Applet applet) { super(x, y, background, applet); this.width = applet.getWidth(); this.height = applet.getHeight(); this.images = foreground; this.currentImage = 0; if (this.images.length == 0) { this.foreground = false; } else { this.foreground = true; } } public void update() { if (this.active && this.foreground) { this.currentImage = (this.currentImage + 1) % this.images.length; } this.updatePosition(); } public void paint(Graphics g) { if (this.visible) { if (this.image != null) g.drawImage(this.image, this.locx, this.locy, this.applet); if (this.foreground) { g.drawImage(this.images[currentImage], this.locx, this.locy, this.applet // the ImageObserver ); } } } public void setPosition(int x, int y) { this.locx = x; this.locy = y; } protected int vx, vy; public void setVelocity(int x, int y) { this.vx = x; this.vy = y; } public void updatePosition() { this.locx += vx; this.locy += vy; this.vx = 0; this.vy = 0; } }
Now let's put together the HTML.import java.applet.*; import java.awt.*; import java.awt.event.*; public class UFOControl extends Applet implements Runnable, KeyListener { Graphics offscreen; Image offscreenImage; static final int NUM_SPRITES = 1; static final int REFRESH_RATE = 80; // in milliseconds... Sprite sprites[]; int width, height; public void init() { System.out.println("*** init *** "); this.setBackground(Color.white); this.width = this.getBounds().width; this.height = this.getBounds().height; this.initSprites(); this.offscreenImage = this.createImage(width, height); this.offscreen = this.offscreenImage.getGraphics(); this.addKeyListener(this); } public void keyPressed (KeyEvent e) { System.out.println("Key Pressed"); switch (e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("Up!"); ((Moveable)this.sprites[0]).setVelocity(0, -3); break; case KeyEvent.VK_RIGHT: System.out.println("Right!"); ((Moveable)this.sprites[0]).setVelocity(3, 0); break; case KeyEvent.VK_DOWN: System.out.println("Down!"); ((Moveable)this.sprites[0]).setVelocity(0, 3); break; case KeyEvent.VK_LEFT: System.out.println("Left!"); ((Moveable)this.sprites[0]).setVelocity(-3, 0); break; } } public void keyReleased(KeyEvent e) { /* do nothing */ } public void keyTyped (KeyEvent e) { /* do nothing */ } public void initSprites() { sprites = new Sprite[NUM_SPRITES]; Image backImage = null; Image foreImage[] = new Image[10]; // ten Duke bitmaps MediaTracker t = new MediaTracker(this); // need tutorial on this try { System.out.println("Getting started."); for (int i = 1; i <= 10; i++) { String imageName = "images/T" + i + ".gif"; foreImage[i - 1] = getImage(getCodeBase(), imageName); t.addImage(foreImage[i - 1], 0); } System.out.println("Loading images."); t.waitForAll(); } catch (Exception e) { System.out.println(e); return; } if (t.isErrorAny()) { System.out.println("error"); } else if (t.checkAll()) { System.out.println("successfully loaded."); } sprites[0] = new BitmapLoop(13, 17, backImage, foreImage, this); } Thread animation; public void start() { System.out.println("*** start ***"); this.animation = new Thread(this); if (this.animation != null) { this.animation.start(); } } public void run() { while (true) { this.repaint(); this.updateSprites(); try { Thread.sleep(REFRESH_RATE); } catch (Exception e) { } } } public void stop() { System.out.println("*** stop ***"); if (this.animation != null) { this.animation.stop(); // this approach is now deprecated... this.animation = null; } } public void updateSprites() { for (int i = 0; i < sprites.length; i++) { this.sprites[i].update(); } } public void update(Graphics g) { this.paint(g); } public void paint(Graphics g) { offscreen.setColor(Color.white); // why? offscreen.fillRect(0, 0, this.width, this.height); for (int i = 0; i < sprites.length; i++) { this.sprites[i].paint(offscreen); } g.drawImage(this.offscreenImage, 0, 0, this); } }
Also because of<html> <head> <title>Bitmap Loop</title> </head> <body bgcolor=white> <applet code="UFOControl.class" width=300 height=300> </applet> </body> </html>
initSprites()
make sure you have the
following:
So now you're ready to go.frilled.cs.indiana.edu%pwd /nfs/moose/home/user3/dgerman/lect28 frilled.cs.indiana.edu%ls -ld * drwx------ 2 dgerman 512 Aug 5 23:50 images frilled.cs.indiana.edu%du -a 2 ./images/T1.gif 2 ./images/T2.gif 2 ./images/T3.gif 2 ./images/T4.gif 2 ./images/T5.gif 2 ./images/T6.gif 2 ./images/T7.gif 2 ./images/T8.gif 2 ./images/T9.gif 2 ./images/T10.gif 21 ./images 22 . frilled.cs.indiana.edu%
Create a file UFOControl.java
and put everything in it.
Then compile and run the appletviewer on the HTML file.
NOTE: To get the above to compile and run you only need to
import
the right packages.
(So, do it with care and good luck).