| CSCI A201/A597Lab Notes 12 Spring 2000 |
Last lecture on Tuesday we created random circles and drew them on the screen.
Let's implement a program that creates 10 random circles and places them on the screen.
Then waits for the user to press the mouse somewhere in the window.
If the mouse was pressed inside one of the circles then the first circle that contains the point where the mouse was pressed will be following the mouse in its movement while the mouse is being pressed.
As soon as the mouse is released the circle no longer follows the mouse.
How are we going to implement such a thing?
First of all, have we ever seen anything like it?
The answer is: yes, the rubber band was doing just that.
The lines were (both) following the mouse pointer while the mouse button was being kept pressed.
Was there anything special with that example?
Perhaps the way the lines were drawn and erased was more special than anything else.
The window was in a special painting mode, called invertMode().
In that mode if you draw one thing once it appears on the screen and if you draw it again in the same place it disappears and the background is restored. It's a property of the window so if it worked with lines it should work with circles too.
Here now is the program.
import element.*;
import java.util.*;
public class MovingCircles {
public static void main(String[] args) {
Random gen = new Random();
Circle[] circles = new Circle[10];
DrawingWindow d = new DrawingWindow();
d.invertMode();
for (int i = 0; i < 10; i++) {
int x = Math.abs(gen.nextInt()) % 200;
int y = Math.abs(gen.nextInt()) % 200;
int r = Math.abs(gen.nextInt()) % 15 + 5;
circles[i] = new Circle(x, y, r);
d.draw(circles[i]);
}
while (true) {
Pt p = d.awaitMousePress();
boolean circleHit = false;
int index = -1;
for (int i = 0; i < circles.length && !circleHit; i++) {
if (circles[i].contains(p)) {
circleHit = true;
index = i;
}
}
while (d.mousePressed()) {
Pt m = d.getMouse();
if (circleHit) {
d.draw(circles[index]);
m = new Pt(m.x() - circles[index].radius(),
m.y() - circles[index].radius());
circles[index].moveTo(m);
d.draw(circles[index]);
}
}
}
}
}
Here's the Tetris-like game:
import element.*; // for graphics and circles
import java.util.Random; // for random numbers
class T {
public static void main(String[] args) {
Random r = new Random(); // our generator
DrawingWindow d = new DrawingWindow(); d.invertMode();
Rect le = new Rect(0, 0, 10, 10), // wind blowing towards left (east)
ri = new Rect(190, 0, 10, 10); // wind blowing towards right (west)
d.draw(le); d.draw(ri); // placing the mouse in these rectangles amounts to that
Line left, bottom, right; // creating the bin
left = new Line(new Pt(20, 40), new Pt(20, 180)); // left margin
right = new Line(new Pt(180, 40), new Pt(180, 180)); // right margin
bottom = new Line(new Pt(20, 180), new Pt(180, 180)); // bottom of bin
d.draw(left); d.draw(right); d.draw(bottom); // draw it
boolean gameGoesOn = true; // originally we want to start the game
Circle[] c = new Circle[200]; // so many circles
int i = 0; // currently working on circle i
double score = 0; // your score is the percent of area covered
while (gameGoesOn) {
int radius = Lib.rand(r, 10, 20); // give me a random size
score += 3.14 * radius * radius; // score increased by the area of the circle
c[i] = new Circle(Lib.rand(r, 20 + radius, 180 - radius), 40, radius); // the circle
d.draw(c[i]); // draw it, where it starts falling
boolean circleMoving = true; // before anything it moves
for (int j = 0; j < i; j++)
if (Lib.overlap(c[i], c[j]))
circleMoving = false; // movement stops if it overlaps any other circle
while (circleMoving) { // here's how the circle moves if it should be moving
Lib.wait(0.5); // wait half a second
d.draw(c[i]); // then draw it (again) thus erasing it
c[i].move(0, 3); // then move it 3 pixels down
if (le.contains(d.getMouse())) { // it should also move three pixels to the left
c[i].move(-3, 0); // if the wind is blowing in that direction
} else if (ri.contains(d.getMouse())) { // or three pixels to the right
c[i].move(3, 0); // if the wind is blowing in that direction
}
d.draw(c[i]); // now draw it (for the first time) in this
// new position
// we now asses if the movement should continue
for (int j = 0; j < i; j++) {
if (Lib.overlap(c[i], c[j])) {
// if circles overlap the movement of this circle should stop
circleMoving = false;
}
}
if (c[i].center().y() + c[i].radius() > 180
|| (c[i].center().x() - c[i].radius() < 20)
|| (c[i].center().x() + c[i].radius() > 180)) {
// the falling of a circle also stops if it hits the border
circleMoving = false;
}
} // end of while if we reach this place the movement of circlec[i] has ended
if (i == 200 - 1 || c[i].center().y() < 80) {
// the game stops when the user has placed all 200 circles
//(s)he's allowed to place or the stack of circles too tall
gameGoesOn = false;
}
// c[i] becomes permanent and gets drawn filled
d.paintMode();
d.fill(c[i]);
d.invertMode();
i += 1; // getting ready to work with a new circle, with an index one higher
}
System.out.println("Game over.");
System.out.println("Final score: " + (score / 22400));
// divide the covered area by the total area for the score
}
}
class Lib {
public static int rand(Random gen, int left, int right) {
return Math.abs(gen.nextInt()) % (right - left + 1) + left;
}
public static boolean overlap(Circle a, Circle b) {
return Lib.distance(a.center(), b.center()) < a.radius() + b.radius();
}
public static double distance(Pt a, Pt b) {
int h, v;
h = Math.abs(a.x() - b.x());
v = Math.abs(a.y() - b.y());
return Math.sqrt(h * h + v * v);
}
public static void wait(double s) {
long now, then;
now = System.currentTimeMillis();
then = now + (long)(s * 1000);
while (System.currentTimeMillis() < then) {
}
}
}