There's a breakout game that's been partially created for me; currently it only has a bouncing ball and a bat; there are no bricks which the ball can hit.
I need to add code to generate bricks but I'm struggling; I have no idea how to approach this as I'm not very good with Java GUI.
I have included the classes where the code needs to be added; the areas which need the code are written in comments
Modelbreakout class:
package breakout;
import java.util.Observable;
import static breakout.Global.*;
/**
* Model of the game of breakout
* The active object ActiveModel does the work of moving the ball
* #author Mike Smith University of Brighton
*/
public class ModelBreakout extends Observable
{
private GameObject ball; // The ball
private GameObject bricks[]; // The bricks
private GameObject bat; // The bat
private ModelActivePart am = new ModelActivePart( this );
private Thread activeModel = new Thread( am );
private int score = 0;
public void createGameObjects()
{
ball = new GameObject(W/2, H/2, BALL_SIZE, BALL_SIZE, Colour.RED );
bat = new GameObject(W/2, H - BRICK_HEIGHT*4, BRICK_WIDTH*3,
BRICK_HEIGHT, Colour.GRAY);
bricks = new GameObject[BRICKS];
// *[1]**********************************************************
// * Fill in code to place the bricks on the board *
// **************************************************************
}
public void startGame() { activeModel.start(); }
public GameObject getBall() { return ball; }
public GameObject[] getBricks() { return bricks; }
public GameObject getBat() { return bat; }
public void addToScore( int n ) { score += n; }
public int getScore() { return score; }
public void stopGame() { }
/**
* Move the bat dist pixels. (-dist) is left or (+dist) is right
* #param dist - Distance to move
*/
public void moveBat( float dist )
{
// *[2]**********************************************************
// * Fill in code to prevent the bat being moved off the screen *
// **************************************************************
Debug.trace( "Model: Move bat = %6.2f", dist );
bat.moveX(dist);
//modelChanged();
}
/**
* Model has changed so notify observers so that they
* can redraw the current state of the game
*/
public void modelChanged()
{
setChanged(); notifyObservers();
}
}
ViewBreakout class:
package breakout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import static breakout.Global.*;
/**
* Displays a graphical view of the game of breakout
* Uses Garphics2D would need to be re-implemented for Android
* #author Mike Smith University of Brighton
*/
public class ViewBreakout extends JFrame implements Observer
{
private ControllerBreakout controller;
private GameObject ball; // The ball
private GameObject[] bricks; // The bricks
private GameObject bat; // The bat
private int score = 0; // The score
private long timeTaken = 0; // How long
private int frames = 0; // Frames output
private final static int RESET_AFTER = 200;
/**
* Construct the view of the game
*/
public ViewBreakout()
{
setSize( W, H ); // Size of window
addKeyListener( new Transaction() ); // Called when key press
setDefaultCloseOperation(EXIT_ON_CLOSE);
Timer.startTimer();
}
/**
* Code called to draw the current state of the game
* Uses draw: Draw a shape
* fill: Fill the shape
* setPaint: Colour used
* drawString: Write string on display
* #param g Graphics context to use
*/
public void drawActualPicture( Graphics2D g )
{
frames++;
// White background
g.setPaint( Color.white );
g.fill( new Rectangle2D.Float( 0, 0, W, H ) );
Font font = new Font("Monospaced",Font.BOLD,24);
g.setFont( font );
// Blue playing border
g.setPaint( Color.blue ); // Paint Colour
g.draw( new Rectangle2D.Float( B, M, W-B*2, H-M-B ) );
// Display the ball
display( g, ball );
// Display the bricks that make up the game
// *[3]**********************************************************
// * Fill in code to display bricks (A brick may not exist) *
// **************************************************************
// Display the bat
display( g, bat );
// Display state of game
g.setPaint( Color.black );
FontMetrics fm = getFontMetrics( font );
String fmt = "BreakOut: Score = [%6d] fps=%5.1f";
String text = String.format(fmt, score,
frames/(Timer.timeTaken()/1000.0)
);
if ( frames > RESET_AFTER )
{ frames = 0; Timer.startTimer(); }
g.drawString( text, W/2-fm.stringWidth(text)/2, (int)M*2 );
}
private void display( Graphics2D g, GameObject go )
{
switch( go.getColour() )
{
case GRAY: g.setColor( Color.gray );
break;
case BLUE: g.setColor( Color.blue );
break;
case RED: g.setColor( Color.red );
break;
}
g.fill( new Rectangle2D.Float( go.getX(), go.getY(),
go.getWidth(), go.getHeight() ) );
}
/**
* Called from the model when its state has changed
* #param aModel Model to be displayed
* #param arg Any arguments
*/
#Override
public void update( Observable aModel, Object arg )
{
ModelBreakout model = (ModelBreakout) aModel;
// Get from the model the ball, bat, bricks & score
ball = model.getBall(); // Ball
bricks = model.getBricks(); // Bricks
bat = model.getBat(); // Bat
score = model.getScore(); // Score
//Debug.trace("Update");
repaint(); // Re draw game
}
/**
* Called by repaint to redraw the Model
* #param g Graphics context
*/
#Override
public void update( Graphics g ) // Called by repaint
{
drawPicture( (Graphics2D) g ); // Draw Picture
}
/**
* Called when window is first shown or damaged
* #param g Graphics context
*/
#Override
public void paint( Graphics g ) // When 'Window' is first
{ // shown or damaged
drawPicture( (Graphics2D) g ); // Draw Picture
}
private BufferedImage theAI; // Alternate Image
private Graphics2D theAG; // Alternate Graphics
public void drawPicture( Graphics2D g ) // Double buffer
{ // to avoid flicker
if ( theAG == null )
{
Dimension d = getSize(); // Size of curr. image
theAI = (BufferedImage) createImage( d.width, d.height );
theAG = theAI.createGraphics();
}
drawActualPicture( theAG ); // Draw Actual Picture
g.drawImage( theAI, 0, 0, this ); // Display on screen
}
/**
* Need to be told where the controller is
* #param aPongController The controller used
*/
public void setController(ControllerBreakout aPongController)
{
controller = aPongController;
}
/**
* Methods Called on a key press
* calls the controller to process
*/
class Transaction implements KeyListener // When character typed
{
#Override
public void keyPressed(KeyEvent e) // Obey this method
{
// Make -ve so not confused with normal characters
controller.userKeyInteraction( -e.getKeyCode() );
}
#Override
public void keyReleased(KeyEvent e)
{
// Called on key release including specials
}
#Override
public void keyTyped(KeyEvent e)
{
// Send internal code for key
controller.userKeyInteraction( e.getKeyChar() );
}
}
}
ModelActivePart class:
package breakout;
import static breakout.Global.*;
/**
* A class used by the model to give it an active part.
* Which moves the ball every n millesconds and implements
* an appropirate action on a collision involving the ball.
* #author Mike Smith University of Brighton
*/
public class ModelActivePart implements Runnable
{
private ModelBreakout model;
private boolean runGame = true; // Assume write to is atomic
public ModelActivePart(ModelBreakout aBreakOutModel)
{
model = aBreakOutModel;
}
/**
* Stop game, thread will finish
*/
public void stopGame() { runGame = false; }
/**
* Code to position the ball after time interval
* and work out what happens next
*/
#Override
public void run()
{
final float S = 6; // Units to move the ball
try
{
GameObject ball = model.getBall(); // Ball in game
GameObject bricks[] = model.getBricks(); // Bricks
GameObject bat = model.getBat(); // Bat
while (runGame)
{
double x = ball.getX();
double y = ball.getY();
// Deal with possible edge of board hit
if (x >= W - B - BALL_SIZE) ball.changeDirectionX();
if (x <= 0 + B ) ball.changeDirectionX();
if (y >= H - B - BALL_SIZE)
{
ball.changeDirectionY(); model.addToScore( HIT_BOTTOM );
}
if (y <= 0 + M ) ball.changeDirectionY();
ball.moveX(S); ball.moveY(S);
// As only a hit on the bat/ball is detected it is assumed to be
// on the top or bottom of the object
// A hit on the left or right of the object
// has an interesting affect
boolean hit = false;
// *[4]**********************************************************
// * Fill in code to check if a brick has been hit *
// * Remember to remove a brick in the array (if hit) *
// * [remove] - set the array element to null *
// **************************************************************
if (hit)
ball.changeDirectionY();
if (bat.hitBy(ball) == GameObject.Collision.HIT)
ball.changeDirectionY();
model.modelChanged(); // Model changed refresh screen
Thread.sleep(20); // About 50 Hz
}
} catch (Exception e)
{
Debug.error("ModelActivePart - stopped\n%s", e.getMessage() );
}
}
}
Now I don't expect you to do everything for me, I just want to know how to draw one brick on the screen; from there onwards I could probably to the rest myself.
If you want the whole package; the download link to my project is here
Any help is appreciated :)
For a blue brick at position 0, 0 modify the createGameObjects method of the ModelBreakout class:
public void createGameObjects()
{
ball = new GameObject(W / 2, H / 2, BALL_SIZE, BALL_SIZE, Colour.RED);
bat = new GameObject(W / 2, H - BRICK_HEIGHT * 4, BRICK_WIDTH * 3,
BRICK_HEIGHT, Colour.GRAY);
bricks = new GameObject[BRICKS];
// *[1]**********************************************************
// * Fill in code to place the bricks on the board *
// **************************************************************
bricks[0] = new GameObject(0, 0, BRICK_HEIGHT, BRICK_WIDTH, Colour.BLUE);
}
Then draw the bricks in the drawActualPicture method of the ViewBreakout class:
// Display the bricks that make up the game
// *[3]**********************************************************
// * Fill in code to display bricks (A brick may not exist) *
// **************************************************************
for (GameObject brick : bricks)
{
if (null != brick)
{
display ( g, brick );
}
}
This is a great assignment if it's coursework.
Related
I created a simple GUI using swing in Netbeans. The idea is user chooses a shape, enters dimensions, clicks the button to render a GLUT shape in the internal panel (in green) on the GUI. I'm stuck at trying to get the shapes to render on the inner panel.
What is a good method or example to draw or render shapes onto an internal panel or canvas of a GUI?
GUI layout for reference:
Update:
Shape Class
package swing_tests;
import static com.sun.javafx.fxml.expression.Expression.add;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.*;
import com.jogamp.opengl.util.gl2.GLUT;
/**
*
* #author Charlie
*/
public class Shape extends GLJPanel {
/**
* #param args the command line arguments
*/
// fields
int width;
int height;
int length;
String name;
//Constructor
public Shape (int w, int h, int l, String n){
width = w;
height = h;
length = l;
name = n;
}
private final GLUT glut = new GLUT();
// Methods
public void JOGLShapes() {
GLCapabilities caps = new GLCapabilities(null);
GLJPanel display = new GLJPanel(caps);
display.setPreferredSize( new Dimension (50,50));
display.setVisible(true);
display.setBackground(Color.red);
add(display, BorderLayout.CENTER);
}
/////////////////////////////////////
public void display(GLAutoDrawable drawable) {
GL2 gl2 = drawable.getGL().getGL2(); // The object that contains all the OpenGL methods.
gl2.glClear( GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT );
gl2.glLoadIdentity(); // Set up modelview transform.
glut.glutSolidTeapot(2);
}
public void init(GLAutoDrawable drawable) {
// called when the panel is created
GL2 gl = drawable.getGL().getGL2();
gl.glClearColor(0.3F, 0.3F, 0.3F, 1.0F); // TODO: Set background color
gl.glEnable(GL2.GL_DEPTH_TEST); // TODO: Required for 3D drawing, not usually for 2D.
}
public void getArea () {
System.out.println("Area is " + width * height);
}
public void getVolume(){
System.out.println("Volume is "+ width * height * length);
}
public String getName(){
return name;
}
/// Setters
public void setWidth(int newWidth){
width = newWidth;
}
public void setHeight(int newHeight){
height = newHeight;
}
public void setDepth(int newLength){
length = newLength;
}
}
Can any of you FX Gurus tell me why this code appears to freeze FX screen updates? The loop is continuing to run in the animation thread, but after a while the screen stops updating.
I know, by the way, that using Thread.sleep in this way might upset some people, but this is code for students in an intro course to allow them to create animations without doing any event handling.
The exercise for students is to convert the animation to make it bounce an array of 100's of balls moving in random directions. After the conversion, the freeze tends to come much earlier than with a single ball.
Thanks in advance!
Here's the main class...
package week6code;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Bouncing Balls exercise starter
*
* #author Sam Scott
*/
public class BouncingBalls extends Application {
/**
* Sets up the stage and starts the main thread. Your drawing code should
* NOT go here.
*
* #param stage The first stage
*/
#Override
public void start(Stage stage) {
stage.setTitle("Bouncing Balls!"); // window title here
Canvas canvas = new Canvas(400, 300); // canvas size here
Group root = new Group();
Scene scene = new Scene(root);
root.getChildren().add(canvas);
stage.setScene(scene);
stage.show();
GraphicsContext gc = canvas.getGraphicsContext2D();
// This code starts a "thread" which will run your animation
Thread t = new Thread(() -> animate(gc));
t.start();
}
/**
* Animation thread. This is where you put your animation code.
*
* #param gc The drawing surface
*/
public void animate(GraphicsContext gc) {
// YOUR CODE HERE!
// intial positions and speeds
Ball ball = new Ball(100, 50, -1, -1, 10, Color.RED);
while (true) // loop forever
{
// draw screen
gc.setFill(Color.YELLOW);
gc.fillRect(0, 0, 400, 300);
ball.draw(gc);
// moving
ball.moveOneStep();
// bouncing
if (ball.getX() <= 0 || ball.getX() >= 400 - (ball.getSize() - 1)) {
ball.bounceX();
}
if (ball.getY() <= 0 || ball.getY() >= 300 - (ball.getSize() - 1)) {
ball.bounceY();
}
// pause
pause(1000 / 60);
}
}
/**
* Use this method instead of Thread.sleep(). It handles the possible
* exception by catching it, because re-throwing it is not an option in this
* case.
*
* #param duration Pause time in milliseconds.
*/
public static void pause(int duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException ex) {
}
}
/**
* Exits the app completely when the window is closed. This is necessary to
* kill the animation thread.
*/
#Override
public void stop() {
System.exit(0);
}
/**
* Launches the app
*
* #param args unused
*/
public static void main(String[] args) {
launch(args);
}
}
And here's the Ball class.
package week6solutions;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
/**
* An example of an object that can draw and move itself.
*
* #author Sam Scott
*/
public class Ball {
private double x, y, xSpeed, ySpeed;
private final int size;
private final Color c;
/**
* Creates a Ball instance.
*
* #param x Initial x position (left)
* #param y Initial y position (top)
* #param xSpeed Number of pixels to move horizontally in each step
* (negative for left, positive for right)
* #param ySpeed Number of pixels to move vertically in each step (negative
* for up, positive for down)
* #param size Diameter of ball
* #param c Color of ball
*/
public Ball(double x, double y, double xSpeed, double ySpeed, int size, Color c) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.size = size;
this.c = c;
}
/**
* Increment x and y using the values of xSpeed and ySpeed
*/
public void moveOneStep() {
x += xSpeed;
y += ySpeed;
}
/**
* Reverses the x direction by multiplying it by -1
*/
public void bounceX() {
xSpeed *= -1;
}
/**
* Reverses the y direction by multiplying it by -1
*/
public void bounceY() {
ySpeed *= -1;
}
/**
* Draw the ball in its current location on a Graphics object
*
* #param g The GraphicsContext object to draw on
*/
public void draw(GraphicsContext g) {
g.setFill(c);
g.fillOval((int) Math.round(x), (int) Math.round(y), size, size);
}
/**
* #return the current x location
*/
public double getX() {
return x;
}
/**
* #return the current y location
*/
public double getY() {
return y;
}
/**
* #return the size of the ball
*/
public int getSize() {
return size;
}
}
I still don't understand why you insist on teaching to do this the wrong way if the right way is so trivial and in fact very close to your code.
Just replace all the threading stuff with an AnimationTimer. Here is your updated main code. The rest just remains as before.
package week6code;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Bouncing Balls exercise starter
*
* #author Sam Scott
*/
public class BouncingBallsDoneRight extends Application {
/**
* Sets up the stage and starts the main thread. Your drawing code should
* NOT go here.
*
* #param stage The first stage
*/
#Override
public void start(Stage stage) {
stage.setTitle("Bouncing Balls!"); // window title here
Canvas canvas = new Canvas(400, 300); // canvas size here
Group root = new Group();
Scene scene = new Scene(root);
root.getChildren().add(canvas);
stage.setScene(scene);
stage.show();
GraphicsContext gc = canvas.getGraphicsContext2D();
// This code starts an AnimationTimer which will run your animation
AnimationTimer at = new AnimationTimer() {
Ball ball = new Ball(100, 50, -1, -1, 10, Color.RED);
#Override
public void handle(long arg0) {
// draw screen
gc.setFill(Color.YELLOW);
gc.fillRect(0, 0, 400, 300);
ball.draw(gc);
// moving
ball.moveOneStep();
// bouncing
if (ball.getX() <= 0 || ball.getX() >= 400 - (ball.getSize() - 1)) {
ball.bounceX();
}
if (ball.getY() <= 0 || ball.getY() >= 300 - (ball.getSize() - 1)) {
ball.bounceY();
}
}
};
at.start();
}
/**
* Launches the app
*
* #param args unused
*/
public static void main(String[] args) {
launch(args);
}
}
I tried to stay as close to your original code as possible. An even better solution for the task would be to use the scene graph and not the canvas.
The main problem is that JavaFX is not thread-safe (https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm).
That is the reason of freeze. Each access, modification or drawing to UI should be done on UI thread.
That could be done with Platform.runLater().
Platform.runLater(() -> {
/*your code here*/
});
im using the Canvas class to make a screensaver as a schoolproject.
But the window generated by Canvas doesnt show my objects on it (current time)
until i minimize it an resize it again. After that all things works fine.
so what is wrong?
thank you for coming answers!
with kind regards
leon
those are the classes, i peronally think that the problem is in the class Start or BufferedCanvas
import java.awt.*;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class Start
{
int fensterX = 900;
int fensterY = 700;
Farbengenerator fg = new Farbengenerator();
BufferedCanvas c =newBufferedCanvas("Bild",fensterX,fensterY);
Zeit z = new Zeit();
SchriftParameter sp = new SchriftParameter();
public void zeichneText(){
double x = 100;
double y = 100;
double fy =0.01;
double fx =0.02;
int red=0;
int green=0;
int blue=0;
double colourGrowRate=0.05;
String uhr;
c.setFont(sp.setzteSchrift());
c.setForegroundColour(Color.BLACK);
c.setBackgroundColour(Color.WHITE);
for(int i=0;i<100;i++){
c.drawString("Starting...",(int)x,(int)y);
c.updateAndShow();
try{Thread.sleep(50);}
catch(Exception e){};
c.updateAndShow();
}
CreateButton d = new CreateButton();
d.run();
while(true) {
c.erase();
uhr = z.erstelleZeit();
c.drawString(uhr,(int)x,(int)y);
if((int)x >fensterX-93 || (int)x <5){
fx = fx * (-1);
red=fg.gibROT();
green=fg.gibGRUEN();
blue=fg.gibBLAU();
Color colour = new Color(red,green,blue);
c.setForegroundColour(colour);
}
if((int)y > fensterY-1 || (int)y < 46){
fy = fy * (-1);
red=fg.gibROT();
green=fg.gibGRUEN();
blue=fg.gibBLAU();
Color colour = new Color(red,green,blue);
c.setForegroundColour(colour);
}
if((int)colourGrowRate>=1){
fg.generiereFarbe();
colourGrowRate = 0.05;
}
colourGrowRate=colourGrowRate+colourGrowRate;
x = x + fx;
y = y + fy;
c.updateAndShow();
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;
public class BufferedCanvas
{
private JFrame frame;
private CanvasPane canvas;
private Graphics2D graphic;
private Color backgroundColour;
private Image canvasImage;
BufferStrategy buff;
/**
* Create a BufferedCanvas with default height,
width and background colour
* (300, 300, white).
* #param title title to appear in Canvas Frame
*/
public BufferedCanvas(String title)
{
this(title, 300, 300, Color.white);
}
/**
* Create a BufferedCanvas with default background colour (white).
* #param title title to appear in Canvas Frame
* #param width the desired width for the canvas
* #param height the desired height for the canvas
*/
public BufferedCanvas(String title, int width, int height)
{
this(title, width, height, Color.white);
}
/**
* Create a BufferedCanvas.
* #param title title to appear in Canvas Frame
* #param width the desired width for the canvas
* #param height the desired height for the canvas
* #param bgClour the desired background colour of the canvas
*/
public BufferedCanvas(String title, int width, int height, Color bgColour)
{
frame = new JFrame();
canvas = new CanvasPane();
frame.setContentPane(canvas);
frame.setTitle(title);
canvas.setPreferredSize(new Dimension(width, height));
backgroundColour = bgColour;
frame.pack();
frame.createBufferStrategy(2);
buff = frame.getBufferStrategy();
graphic = (Graphics2D)buff.getDrawGraphics();
setVisible(true);
}
/**
* Set the canvas visibility and brings canvas to the front of screen
* when made visible. This method can also be used to bring an already
* visible canvas to the front of other windows.
* #param visible boolean value representing the desired visibility of
* the canvas (true or false)
*/
public void setVisible(boolean visible)
{
if(graphic == null) {
// first time: instantiate the offscreen image and fill it with
// the background colour
Dimension size = canvas.getSize();
canvasImage = canvas.createImage(size.width, size.height);
graphic = (Graphics2D)canvasImage.getGraphics();
graphic.setColor(backgroundColour);
graphic.fillRect(0, 0, size.width, size.height);
graphic.setColor(Color.black);
}
frame.setVisible(true);
}
/**
* Update the canvas and show the new image.
*/
public void updateAndShow(){
buff.show();
}
/**
* Provide information on visibility of the Canvas.
* #return true if canvas is visible, false otherwise
*/
public boolean isVisible()
{
return frame.isVisible();
}
/**
* Draw a given shape onto the canvas.
* #param shape the shape object to be drawn on the canvas
*/
public void draw(Shape shape)
{
graphic.draw(shape);
//canvas.repaint();
}
/**
* Fill the internal dimensions of a given shape with the current
* foreground colour of the canvas.
* #param shape the shape object to be filled
*/
public void fill(Shape shape)
{
graphic.fill(shape);
//canvas.repaint();
}
/**
* Erase the whole canvas.
*/
public void erase()
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
Dimension size = canvas.getSize();
graphic.fill(new Rectangle(0, 0, size.width, size.height));
graphic.setColor(original);
//canvas.repaint();
}
/**
* Erase a given shape's interior on the screen.
* #param shape the shape object to be erased
*/
public void erase(Shape shape)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.fill(shape); // erase by filling background colour
graphic.setColor(original);
//canvas.repaint();
}
/**
* Erases a given shape's outline on the screen.
* #param shape the shape object to be erased
*/
public void eraseOutline(Shape shape)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.draw(shape); // erase by drawing background colour
graphic.setColor(original);
//canvas.repaint();
}
/**
* Draws an image onto the canvas.
* #param image the Image object to be displayed
* #param x x co-ordinate for Image placement
* #param y y co-ordinate for Image placement
* #return returns boolean value representing whether the image was
* completely loaded
*/
public boolean drawImage(Image image, int x, int y)
{
boolean result = graphic.drawImage(image, x, y, null);
//canvas.repaint();
return result;
}
/**
* Draws a String on the Canvas.
* #param text the String to be displayed
* #param x x co-ordinate for text placement
* #param y y co-ordinate for text placement
*/
public void drawString(String text, int x, int y)
{
graphic.drawString(text, x, y);
//canvas.repaint();
}
/**
* Erases a String on the Canvas.
* #param text the String to be displayed
* #param x x co-ordinate for text placement
* #param y y co-ordinate for text placement
*/
public void eraseString(String text, int x, int y)
{
Color original = graphic.getColor();
graphic.setColor(backgroundColour);
graphic.drawString(text, x, y);
graphic.setColor(original);
//canvas.repaint();
}
/**
* Draws a line on the Canvas.
* #param x1 x co-ordinate of start of line
* #param y1 y co-ordinate of start of line
* #param x2 x co-ordinate of end of line
* #param y2 y co-ordinate of end of line
*/
public void drawLine(int x1, int y1, int x2, int y2)
{
graphic.drawLine(x1, y1, x2, y2);
//canvas.repaint();
}
/**
* Draws a dot/pixel on the Canvas.
* #param x x co-ordinate of dot
* #param y y co-ordinate of dot
*/
public void drawDot(int x, int y)
{
graphic.drawLine(x, y, x, y);
//canvas.repaint();
}
/**
* Sets the foreground colour of the Canvas.
* #param newColour the new colour for the foreground of the Canvas
*/
public void setForegroundColour(Color newColour)
{
graphic.setColor(newColour);
}
/**
* Returns the current colour of the foreground.
* #return the colour of the foreground of the Canvas
*/
public Color getForegroundColour()
{
return graphic.getColor();
}
/**
* Sets the background colour of the Canvas.
* #param newColour the new colour for the background of the Canvas
*/
public void setBackgroundColour(Color newColour)
{
backgroundColour = newColour;
graphic.setBackground(newColour);
}
/**
* Returns the current colour of the background
* #return the colour of the background of the Canvas
*/
public Color getBackgroundColour()
{
return backgroundColour;
}
/**
* changes the current Font used on the Canvas
* #param newFont new font to be used for String output
*/
public void setFont(Font newFont)
{
graphic.setFont(newFont);
}
/**
* Returns the current font of the canvas.
* #return the font currently in use
**/
public Font getFont()
{
return graphic.getFont();
}
/**
* Sets the size of the canvas.
* #param width new width
* #param height new height
*/
public void setSize(int width, int height)
{
canvas.setPreferredSize(new Dimension(width, height));
Image oldImage = canvasImage;
canvasImage = canvas.createImage(width, height);
graphic = (Graphics2D)canvasImage.getGraphics();
graphic.drawImage(oldImage, 0, 0, null);
frame.pack();
}
/**
* Returns the size of the canvas.
* #return The current dimension of the canvas
*/
public Dimension getSize()
{
return canvas.getSize();
}
/**
* Waits for a specified number of milliseconds before finishing.
* This provides an easy way to specify a small delay which can be
* used when producing animations.
* #param milliseconds the number
*/
public void wait(int milliseconds)
{
try
{
Thread.sleep(milliseconds);
}
catch (Exception e)
{
// ignoring exception at the moment
}
}
/************************************************************************
* Nested class CanvasPane - the actual canvas component contained in the
* Canvas frame. This is essentially a JPanel with added capability to
* refresh the image drawn on it.
*/
private class CanvasPane extends JPanel
{
public void paint(Graphics g)
{
g.drawImage(canvasImage, 0, 0, null);
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class CreateButton extends JFrame implements ActionListener{
public void run() {
createAndShowGUI();
}
public CreateButton() {
// set layout for the frame
this.getContentPane().setLayout(new FlowLayout());
JButton button1 = new JButton();
button1.setText("closeApp");
//set actionlisteners for the buttons
button1.addActionListener(this);
// define a custom short action command for the button
button1.setActionCommand("closeApp");
// add buttons to frame
add(button1);
}
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new CreateButton();
//Display the window.
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent ae) {
String action = ae.getActionCommand();
if (action.equals("closeApp")) {
System.exit(1);
}
}
}
import java.awt.*;
public class SchriftParameter
{
public Font setzteSchrift(){
Font f = new Font("Fixed",1,24);
return (f);
}
}
public class Farbengenerator
{
int r=0;
int g=0;
int b=0;
public void generiereFarbe(){
if (r<255&&g==0&&b==0){
r++;
}
else if (r==255&&g<255&&b==0){
g++;
}
else if (r>0&&g==255&&b==0){
r= r-1;
}
else if (r==0&&g==255&&b<255){
b++;
}
else if (r==0&&g>0&&b==255){
g=g-1;
}
else if (r<255&&g==0&&b==255){
r++;
}
else if (r==255&&g==0&&b>0){
b=b-1;
}
}
public int gibROT () {
return(r);
}
public int gibGRUEN () {
return(g);
}
public int gibBLAU () {
return(b);
}
}
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class Zeit
{
public String erstelleZeit(){
DateFormat df = new SimpleDateFormat("HH:mm:ss");
Date d = new Date();
String uhr = df.format(d);
return (uhr);
}
}
I've messed around with Java a little bit and I decided to take a course to learn some more and I have been assigned a fairly easy assignment. I have it mostly done and I know what's left must be simple, but I cannot seem to quite get it. So far I have been able to successfully create a program that has 1 bouncing ball, but I would now like to allow the user to input the amount of balls to bounce. I have tried a few different loops in my Ball class, but none of them work. Would anyone be willing to give a quick hand? I am almost sure it would require either an Array or ArrayList and just storing the balls in it but I have yet to find a solution that works. I have looked at other problems like this on the website but none have quite solved my problem. Thank you if you can help!
Main Class :
public class mainClass {
/**
/**
* Frame to hold a bouncing ball panel, implemented in the BallPanel class.
* Controls the animation of the ball via pauses and calls to BallPanel's move
* method.
*
* #author Michael Peterson modified by Mr O Aug 2012
*/
public static class BallTest extends JFrame {
// size of the window
private static final int WINDOW_WIDTH = 500;
private static final int WINDOW_HEIGHT = 300;
// panel containing the bouncing ball
private BallPanel ballPanel;
/**
* Pause command used to control the speed of the bouncing ball animation.
* Currently pauses for 20 ms. Use smaller values for faster animation and
* vice versa.
*/
public static void pause() {
try {
Thread.sleep(20); // pause for 20 ms
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}//end of catch
}//end of pause method
/**
* Creates a new instance of BallTest
*/
public BallTest() {
super("Bouncing Ball"); // set frame name
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setLayout(new BorderLayout());
ballPanel = new BallPanel();
add(ballPanel);
center(this);
setVisible(true);
// infinite animation loop, program halts when window is closed.
while (true) {
pause();
ballPanel.move(ballPanel);
}//end of while loop of animation
} //end of BallTest Constructor
/**
* Helper routine to center a frame on the screen (will cause problems if
* frame is bigger than the screen!)
*/
public static void center(JFrame frame) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point center = ge.getCenterPoint();
int w = frame.getWidth();
int h = frame.getHeight();
int x = center.x - w / 2, y = center.y - h / 2;
frame.setBounds(x, y, w, h);
frame.validate();
}//end of center method
}//end of BallPanel Class
public static void main(String[] args) {
BallTest t = new BallTest(); //make a BallTest object
}//end of main method
}//end of Fall2012Lab11StarterCode class
Ball Class :
public class Ball extends JPanel {
private int bxCoord; //the ball's x coordinate
private int byCoord; //the ball's y coordinate
private int bHeight; //the ball's height
private int bWidth; //the ball's weight
private int bRise; //the ball's y change
private int bRun; //the ball's x change
private Color bColor; //the ball's color
//Constructor
public Ball() {
bxCoord = setStartBxCoord();
byCoord = setStartByCoord();
bHeight = setStartBHeight();
bWidth = setStartBWidth();
bRise = setStartBRise();
bRun = setStartBRun();
bColor = setStartColor();
}
/**
* The setters, getters, and initial value for the ball's x coordinate
*/
public void setBxCoord(int xCoord) {
bxCoord = xCoord;
}
public int setStartBxCoord() {
int xCoord;
xCoord = (int) (Math.random() * 51);
return xCoord;
}
public int getBxCoord() {
return bxCoord;
}
/**
* The setters, getters, and initial value for the ball's y coordinate
*/
public void setByCoord(int yCoord) {
bxCoord = yCoord;
}
public int setStartByCoord() {
int yCoord;
yCoord = (int) (Math.random() * 51);
return yCoord;
}
public int getByCoord() {
return byCoord;
}
/**
* The setters, getters, and initial value for the ball's x height
*/
public void setBHeight(int height) {
bHeight = height;
}
public int setStartBHeight() {
int height;
height = (int) (10 + Math.random() * 11);
return height;
}
public int getBHeight() {
return bHeight;
}
public void setBWidth(int width) {
bWidth = width;
}
/**
* The setters, getters, and initial value for the ball's x width
*/
public int setStartBWidth() {
int width;
width = (int) (10 + Math.random() * 11);
return width;
}
public int getBWidth() {
return bWidth;
}
/**
* The setters, getters, and initial value for the ball's rise
*/
public void setBRise(int rise) {
bRise = rise;
}
public int setStartBRise() {
int rise;
rise = (int) (Math.random() * 11);
return rise;
}
public int getBRise() {
return bRise;
}
/**
* The setters, getters, and initial value for the ball's run
*/
public void setBRun(int run) {
bRun = run;
}
public int setStartBRun() {
int run;
run = (int) (Math.random() * 11);
return run;
}
public int getBRun() {
return bRun;
}
/**
* The movement of the ball in the x and y direction
*/
public void moveX(){
bxCoord += bRun;
}
public void moveY(){
byCoord += bRise;
}
/**
* The setters, getters, and initial value for the ball's color
*/
public void setColor(Color color) {
bColor = color;
}
public Color setStartColor() {
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
Color ranColor = new Color(red, green, blue);
return ranColor;
}
public Color getbColor() {
return bColor;
}
/**
* Computes the next position for the balls and updates their positions.
*/
public void move(BallPanel ballPanel) {
// If ball is approaching a wall, reverse direction
if ((getBxCoord() < (0 - getBRun())) || (getBxCoord() > (ballPanel.getWidth() - getBWidth()))) {
setBRun(-getBRun());
}
if ((getByCoord() < (0 - getBRise())) || (getByCoord() > (ballPanel.getHeight() - getBHeight()))) {
setBRise(-getBRise());
}
// "Move" ball according to values in rise and run
moveX();
moveY();
} // end method move
}//end of Ball Class
Ball Panel Class :
public class BallPanel extends JPanel {
Ball ball = new Ball(); //creat a ball.
/**
* Creates a new instance of BallPanel
*/
public BallPanel() {
super();
}
/**
* The move method moves the ball and repaints the panel
* PreCondtion: A panel containing a ball has been created
* PostCondition: The position of a ball on the panel has moved. The panell
* is repainted with the ball in the new position.
* #param ballPanel The name of the panel on which the ball is found
*/
public void move(BallPanel ballPanel) {
ball.move(ballPanel);
repaint();
}
/**
* Paints the balls at their current positions within the panel.
* PreCondition: A graphics object has been created and needs to be displayed
* PostCondition: The graphics object g has been displayed
* #param g The graphic object to be displayed
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black); // set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
// Paint the Ball
g.setColor(ball.getbColor());
g.fillOval(ball.getBxCoord(), ball.getByCoord(),
ball.getBWidth(), ball.getBHeight());
}//end of paintComponent method
}//end of BallPanel class
Well I'm answering you purely on previous codes i have done similar to this, because I didn't have the time to test out yours.
Your ballPanel class should look something more like this:
import java.util.ArrayList;
public class BallPanel extends JPanel{
private ArrayList<Ball> BallList = new ArrayList<Ball>();
private int num;
public BallPanel(int numberOfBalls){
super();
num = numberOfBalls;
for(int i = 0; i<num; i++){BallList.add(new Ball());}
}
//the rest of your methods, using for loops for the balls
Also I think you can use this instead of ArrayList (this is easier):
Ball[] BallList = new Ball[numberOfBalls];
then an example of your move method should look like this:
public void move(BallPanel ballPanel){
for(int i = 0; i<num; i++){
BallList[i].move(ballPanel);
}
repaint();
}
I had a similar issues with a Bouncing Ball program. Tried the previously posted code, but the public void move(BallPanel area isn't working. When I access an array in that location the ball stops moving. Here is my current code for move:
public void move(BallPanel ballPanel, ArrayList<Ball> ballList) {
for(int i = 0; i<1; i++){
System.out.println("mmm" + i);
ballList.get(i).move(ballPanel);
}
repaint();
Secondly as shown the paintComponent area above, only ball is used to paint the ball. Is that OK that paintComponent is called multiple times but only has a variable ball in it. Here is what that section looks like:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black); // set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
// Paint the Ball
g.setColor(ball.getbColor());
g.fillOval(ball.getBxCoord(), ball.getByCoord(),
ball.getBWidth(), ball.getBHeight());
I'm having a lot of trouble trying to make my code move a player (Ship) around a screen. I can get the planets to draw on the screen and the the player ship but I can not figure out how to implement the keyListener to at least print out something. Thank you in advance for all the help!
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class MapPanel extends JPanel {
public static final int WIDTH = 25;
public static final int HEIGHT = 20;
int zone = 0;
private int xValue;
private int yValue;
private Color color;
public Planet[][] planetGrid = new Planet[WIDTH][HEIGHT];
static Player currPlayer = new Player("h");
static Universe universe = new Universe(currPlayer);
/**
* Create the panel.
*/
public MapPanel(Universe univ, Player p) {
this.universe = univ;
currPlayer = p;
int i = 0;
this.setSize(new Dimension(450,450));
setVisible( true );
//this.addKeyListener(new KeyController());
KeyController kc = new KeyController();
this.addKeyListener(kc);
repaint();
}
/**
* Draw method to draw the playing field
* #param g Graphics object
* #param tileDimension dimension of the tile
*/
public void draw(Graphics g)
{
universe.draw(g);
// KeyController key = new KeyController();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public static void main(String[] args)
{
MapPanel mp = new MapPanel(universe, currPlayer);
JFrame f = new JFrame();
f.add(mp);
f.setSize(new Dimension(450,450));
f.setVisible(true);
f.setFocusable(true);
}
private class KeyController implements KeyListener {
public KeyController()
{
System.out.println("ghgh");
setFocusable(true);
// addKeyListener(this);
}
#Override
public void keyPressed(final KeyEvent key) {
System.out.println("fgfgf");
if (currPlayer != null) {
int oldX = currPlayer.getPosition().x;
int oldY = currPlayer.getPosition().y;
switch (key.getKeyCode()) {
case KeyEvent.VK_RIGHT:
currPlayer.setPosition(new Point(oldX+1, oldY)); //move right
System.out.println("RIGHT");
break;
case KeyEvent.VK_LEFT:
currPlayer.setPosition(new Point(oldX-1, oldY)); //move left
break;
case KeyEvent.VK_DOWN:
currPlayer.setPosition(new Point(oldX, oldY+1)); //move down
break;
case KeyEvent.VK_UP:
currPlayer.setPosition(new Point(oldX, oldY-1)); //move up
break;
}
}
repaint();
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("ggg");
}
#Override
public void keyTyped(KeyEvent e) {
System.out.println("typeeeddd");
}
}
}
KeyListener is not the most appropriate method to achieve your results.
It would be better to use the key bindings API.
Apart from simplifying the code, it will also allow you to provide better focus control over when the keys should triggered.
here i have design an application similar to yours,where you can use arrow keys,n throw a bomb on the moving ship(this was my project few years back :) ),design a main method to run,hope this will help:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* This panel implements a simple arcade game in which the user tries to blow
* up a "submarine" (a black oval) by dropping "depth charges" (a red disk) from
* a "boat" (a blue roundrect). The user moves the boat with the left- and
* right-arrow keys and drops the depth charge with the down-arrow key.
* The sub moves left and right erratically along the bottom of the panel.
*/
public class SubKillerPanel extends JPanel {
private Timer timer; // Timer that drives the animation.
private int width, height; // The size of the panel -- the values are set
// the first time the paintComponent() method
// is called. This class is not designed to
// handle changes in size; once the width and
// height have been set, they are not changed.
// Note that width and height cannot be set
// in the constructor because the width and
// height of the panel have not been set at
// the time that the constructor is called.
private Boat boat; // The boat, bomb, and sub objects are defined
private Bomb bomb; // by nested classes Boat, Bomb, and Submarine,
private Submarine sub; // which are defined later in this class.
// Note that the objects are created in the
// paintComponent() method, after the width
// and height of the panel are known.
/**
* The constructor sets the background color of the panel, creates the
* timer, and adds a KeyListener, FocusListener, and MouseListener to the
* panel. These listeners, as well as the ActionListener for the timer
* are defined by anonymous inner classes. The timer will run only
* when the panel has the input focus.
*/
public SubKillerPanel() {
setBackground(Color.GREEN);
ActionListener action = new ActionListener() {
// Defines the action taken each time the timer fires.
public void actionPerformed(ActionEvent evt) {
if (boat != null) {
boat.updateForNewFrame();
bomb.updateForNewFrame();
sub.updateForNewFrame();
}
repaint();
}
};
timer = new Timer( 30, action ); // Fires every 30 milliseconds.
addMouseListener( new MouseAdapter() {
// The mouse listener simply requests focus when the user
// clicks the panel.
public void mousePressed(MouseEvent evt) {
requestFocus();
}
} );
addFocusListener( new FocusListener() {
// The focus listener starts the timer when the panel gains
// the input focus and stops the timer when the panel loses
// the focus. It also calls repaint() when these events occur.
public void focusGained(FocusEvent evt) {
timer.start();
repaint();
}
public void focusLost(FocusEvent evt) {
timer.stop();
repaint();
}
} );
addKeyListener( new KeyAdapter() {
// The key listener responds to keyPressed events on the panel. Only
// the left-, right-, and down-arrow keys have any effect. The left- and
// right-arrow keys move the boat while down-arrow releases the bomb.
public void keyPressed(KeyEvent evt) {
int code = evt.getKeyCode(); // Which key was pressed?
if (code == KeyEvent.VK_LEFT) {
// Move the boat left. (If this moves the boat out of the frame, its
// position will be adjusted in the boat.updateForNewFrame() method.)
boat.centerX -= 15;
}
else if (code == KeyEvent.VK_RIGHT) {
// Move the boat right. (If this moves boat out of the frame, its
// position will be adjusted in the boat.updateForNewFrame() method.)
boat.centerX += 15;
}
else if (code == KeyEvent.VK_DOWN) {
// Start the bomb falling, if it is not already falling.
if ( bomb.isFalling == false )
bomb.isFalling = true;
}
}
} );
} // end constructor
/**
* The paintComponent() method draws the current state of the game. It
* draws a gray or cyan border around the panel to indicate whether or not
* the panel has the input focus. It draws the boat, sub, and bomb by
* calling their respective draw() methods.
*/
public void paintComponent(Graphics g) {
super.paintComponent(g); // Fill panel with background color, green.
if (boat == null) {
// The first time that paintComponent is called, it assigns
// values to the instance variables.
width = getWidth();
height = getHeight();
boat = new Boat();
sub = new Submarine();
bomb = new Bomb();
}
if (hasFocus())
g.setColor(Color.CYAN);
else {
g.setColor(Color.RED);
g.drawString("CLICK TO ACTIVATE", 20, 30);
g.setColor(Color.GRAY);
}
g.drawRect(0,0,width-1,height-1); // Draw a 3-pixel border.
g.drawRect(1,1,width-3,height-3);
g.drawRect(2,2,width-5,height-5);
boat.draw(g);
sub.draw(g);
bomb.draw(g);
} // end drawFrame()
/**
* This nested class defines the boat. Note that its constructor cannot
* be called until the width of the panel is known!
*/
private class Boat {
int centerX, centerY; // Current position of the center of the boat.
Boat() { // Constructor centers the boat horizontally, 80 pixels from top.
centerX = width/2;
centerY = 80;
}
void updateForNewFrame() { // Makes sure boat has not moved off screen.
if (centerX < 0)
centerX = 0;
else if (centerX > width)
centerX = width;
}
void draw(Graphics g) { // Draws the boat at its current location.
g.setColor(Color.BLUE);
g.fillRoundRect(centerX - 40, centerY - 20, 80, 40, 20, 20);
}
} // end nested class Boat
/**
* This nested class defines the bomb.
*/
private class Bomb {
int centerX, centerY; // Current position of the center of the bomb.
boolean isFalling; // If true, the bomb is falling; if false, it
// is attached to the boat.
Bomb() { // Constructor creates a bomb that is initially attached to boat.
isFalling = false;
}
void updateForNewFrame() { // If bomb is falling, take appropriate action.
if (isFalling) {
if (centerY > height) {
// Bomb has missed the submarine. It is returned to its
// initial state, with isFalling equal to false.
isFalling = false;
}
else if (Math.abs(centerX - sub.centerX) <= 36 &&
Math.abs(centerY - sub.centerY) <= 21) {
// Bomb has hit the submarine. The submarine
// enters the "isExploding" state.
sub.isExploding = true;
sub.explosionFrameNumber = 1;
isFalling = false; // Bomb reappears on the boat.
}
else {
// If the bomb has not fallen off the panel or hit the
// sub, then it is moved down 10 pixels.
centerY += 10;
}
}
}
void draw(Graphics g) { // Draw the bomb.
if ( ! isFalling ) { // If not falling, set centerX and CenterY
// to show the bomb on the bottom of the boat.
centerX = boat.centerX;
centerY = boat.centerY + 23;
}
g.setColor(Color.RED);
g.fillOval(centerX - 8, centerY - 8, 16, 16);
}
} // end nested class Bomb
/**
* This nested class defines the sub. Note that its constructor cannot
* be called until the width of the panel is known!
*/
private class Submarine {
int centerX, centerY; // Current position of the center of the sub.
boolean isMovingLeft; // Tells whether the sub is moving left or right
boolean isExploding; // Set to true when the sub is hit by the bomb.
int explosionFrameNumber; // If the sub is exploding, this is the number
// of frames since the explosion started.
Submarine() { // Create the sub at a random location 40 pixels from bottom.
centerX = (int)(width*Math.random());
centerY = height - 40;
isExploding = false;
isMovingLeft = (Math.random() < 0.5);
}
void updateForNewFrame() { // Move sub or increase explosionFrameNumber.
if (isExploding) {
// If the sub is exploding, add 1 to explosionFrameNumber.
// When the number reaches 15, the explosion ends and the
// sub reappears in a random position.
explosionFrameNumber++;
if (explosionFrameNumber == 15) {
centerX = (int)(width*Math.random());
centerY = height - 40;
isExploding = false;
isMovingLeft = (Math.random() < 0.5);
}
}
else { // Move the sub.
if (Math.random() < 0.04) {
// In one frame out of every 25, on average, the sub
// reverses its direction of motion.
isMovingLeft = ! isMovingLeft;
}
if (isMovingLeft) {
// Move the sub 5 pixels to the left. If it moves off
// the left edge of the panel, move it back to the left
// edge and start it moving to the right.
centerX -= 5;
if (centerX <= 0) {
centerX = 0;
isMovingLeft = false;
}
}
else {
// Move the sub 5 pixels to the right. If it moves off
// the right edge of the panel, move it back to the right
// edge and start it moving to the left.
centerX += 5;
if (centerX > width) {
centerX = width;
isMovingLeft = true;
}
}
}
}
void draw(Graphics g) { // Draw sub and, if it is exploding, the explosion.
g.setColor(Color.BLACK);
g.fillOval(centerX - 30, centerY - 15, 60, 30);
if (isExploding) {
// Draw an "explosion" that grows in size as the number of
// frames since the start of the explosion increases.
g.setColor(Color.YELLOW);
g.fillOval(centerX - 4*explosionFrameNumber,
centerY - 2*explosionFrameNumber,
8*explosionFrameNumber,
4*explosionFrameNumber);
g.setColor(Color.RED);
g.fillOval(centerX - 2*explosionFrameNumber,
centerY - explosionFrameNumber/2,
4*explosionFrameNumber,
explosionFrameNumber);
}
}
} // end nested class Submarine
} // end class SubKiller